Sunday, February 27, 2011

Abriendo las puertas de Internet

A veces me da la impresión de que en vez de evolucionar, algunos se empeñan en no hacerlo con la excusa de sacar un supuesto mayor beneficio económico.

Hace ya unos meses que utilizo un Nexus one, uno de tantos teléfonos Android. Lo adquirí mediante una de esas tarifas abusivas con permanencia de un año, y una cuota mensual bastante elevada para el uso que le doy.
Al adquirirlo, las palabras Internet ilimitado aparecían por todas partes, genial pensé, será uno de esos pocos operadores que permiten la voz sobre ip..

No es que sea una persona muy habladora, pero vamos, que si disfrutamos del lujo de tener Internet en nuestras manos, es muy probable que aparezca la ocasión en la que necesitemos realizar una llamada sin que ésta sea tarificada de una manera abusiva.

Me estoy dando cuenta de que poco a poco estoy dando mucho uso a los routers pequeños, por un lado tenemos la fonera haciendo buenas migas con arduino y la lámpara de emociones. Por otro lado tenemos un router Acton que después de ser flasheado con Openwrt es el encargado de mantener vivas las sesiones de IRC mediante Miau, a la vez que sirve de centraliza Asterisk y servidor de vpn PPTP. Y tan sólo utilizando el 50% de sus recursos (memoria y sistema de ficheros).

La centralita Asterisk la tengo conectada a algún proveedor de telefonía ip que me deja las llamadas a fijos de la mayoría de países gratuitas y a móviles a bastante buen precio, por ahora podríamos decir que está funcionando a medio gas, ya que aún tengo pendiente añadirle algún número virtual para poder recibir llamadas entrantes.
Que grande sería poder instalar unas cuantas centralitas asociadas a estos minirouters y proveer de servicio de cabina gratuita mundial en todos los rincones.
13 kbits, eso son los recursos de red que consume una llamada empleando el codec de audio GSM.

Decíamos que uno de los peque routers se encarga de mantener un servidor de vpn, efectivamente, mantiene un servidor para conexiones PPTP, por qué este tipo si no es el más seguro? por simplicidad en su configuración y porque fue el primero que probé :-)
La idea del servidor de vpn se me ocurrió como alternativa al ssh -D bind address, así podría en rutar todos los puertos sin problema y darme una mayor seguridad allá done estuviera conectado.
Pero hace unos días que me picaba la mosca de si sería posible enrutar Voz sobre Ip sobre una VPN casera, en principio los números para el ancho de banda necesario eran favorables, pero había que probarlo, no podía permitir tener esa Internet en mis manos y que un ente abstracto estuviera capando puertos necesarios para la voz sobre ip y conexiones a sus proveedores.

Empecé por la instalación de PPTPD mediante:
opkg update
opkg install pptpd
opkg install kmod-crypto
opkg install kmod-mppe
/etc/init.d/pptpd enable
/etc/init.d/pptpd start

configurando las credenciales de acceso en:
/etc/ppp/chap-secrets

y haciendo el fichero un poco más seguro
chmod 600 /etc/chap-secrets

para comprobar la configuración miré aquí.

Una vez la Vpn ya estaba levantada, teníamos que poner al pequeño router en Internet bien mediante nateo del puerto tcp 1723 y el protocolo GRE. Mi router freebox no me permitía natear un protocolo, sólo puertos así que la única solución viable era utilizar la opción de colocar el servidor de vpn en la zona DMZ de la freebox.

A partir de ahí ya sólo era cuestión de movernos al cliente, en éste caso el móvil android el cual viene con capacidad de realizar conexiones Vpn. Desde el menu settings, wireless & networks, VPN settings , Add VPN , eligiendo PPTP VPN.
Elegí Enable encryption, opción segura pero no demasiado, sin utilizar certificados.

Una vez la vpn estaba levantada desde la conexión 3G sólo me quedaba configurar Sipdroid, aplicación de voz sobre ip opensource para Android, con mi proveedor sip.
De todos los codecs de Audio disponibles en sipdroid, cuando quiero utilizar voip sobre la vpn fuerzo GSM ya que su uso de ancho de banda es limitado. Si estoy en casa utilizando el wifi utilizo algo como G722 o PCMA, codecs que podríamos llamar el High Definition de la voz.

Y voilà, lo que antes no era posible por limitaciones impuestas, ya es posible, mediante un uso mínimo de ancho de banda.
Por fín puedo llamar con tranquilidad a España u otros países desde cualquier lugar, de manera segura y a un precio justo.


Sunday, February 06, 2011

Midiendo el ánimo del mundo

No veo la tele, pocas veces lo hago, ni siquiera cuando estoy en España. Sólo sintonizo algún canal francés via VLC gracias a mi proveedor de adsl (Free) cuando estoy en Lyon, el cual nos deja los 20 megas a 30 euros mensuales.

La verdad es que con la cantidad de información a la que tenemos acceso estos días prefiero intentar filtrar aquello que pueda ser interesante, sin que ese canal de información sea sólo en una dirección.
Así que hace unas semanas me decidí a medir el ánimo mundial basándome en estudios ya comenzados relativos a ésta materia, registrando los eventos más importantes y dando un clima más cálido a la casa, mezclando hardware y software libre con sentimientos reales de personas.

Para poder llevar a cabo el proyecto era necesario estudiar cuáles son las palabras que identifican mejor los sentimientos, me disponía a estudiar las variaciones de la siguiente lista de sentimientos, la clase Tipo de Animo.

class TipoAnimo:
AMOR=0
ALEGRIA=1
SORPRESA=2
IRA=3
ENVIDIA=4
TRISTEZA=5
MIEDO=6
NUM_TIPOS_ANIMO = 7



Así empezaba la preparación del código Python que se encargaría d e enviar las consultas a Twitter para su posterior procesado.
Algunos ejemplos de palabras seleccionadas para la búsqueda de emociones son las siguientes:


AMOR_QUERY = '\"te+quiero+mucho\"+OR+\"te+quiero+más\"+OR+\"amo+tanto\"+[...]
IRA_QUERY='\"te+odio\"+OR+\"siento+rabia\"+OR+\"le+odio\"+OR+\"estoy+furioso\"+[...]
ALEGRIA_QUERY='\"mas+feliz\"+OR+\"bastante+feliz\"+OR+\"tan+feliz\"+OR+[...]
SORPRESA_QUERY='\"no+me+lo+puedo+creer\"+OR+\"increible\"+OR+\"asombro\"+OR+[...
ENVIDIA_QUERY='\"ambiciono\"+OR+\"codicio\"+OR+\"mucha+envidia\"+OR+[...]
TRISTEZA_QUERY='\"muy+triste\"+OR+\"tan+deprimido\"+OR+\"estoy+llorando\"+OR+[...]
MIEDO_QUERY='\"muy+asustado\"+OR+\"tan+asustada\"+OR+\"realmente+asustado\"+OR+[...]


Teníamos entonces las palabras exactas que nos marcarían las emociones. Adecuándolas para evitar falsos positivos del estilo "quiero comer", para ello utilizaremos palabras con adverbios y pronombres, nos sirven para acotar mejor la búsqueda y seleccionar la emoción en cuestión de una manera más exacta.
A modo de ejemplo y para mostrar la importancia de la selección de las palabras, hace unos días me extraño mucho que durante la etapa de estudio siempre había una emoción que no decrecía en intensidad, durante la noche en América del Sur y las primeras horas de la mañana de España, era el miedo, pensé que igual la noche tuviera algo que ver con esta curiosa actitud...

Hasta que me fijé, en que una de las palabras seleccionadas para medir el miedo era "Horror", y viendo los tweets generados por esa palabra había muchos tweets en inglés, normal que el miedo no durmiera en español, los anglo parlantes lo tenía a su merced durante su zona horaria.

Necesitábamos también una magnitud que nos permitiera la medida de las emociones así utilizamos el número de tweets por minuto que gracias a la fantástica API de búsqueda de twitter, podíamos conseguir con una sencilla función en python:

def parse_tps(animoID):
print 'query_dict[animoID]= '+query_dict[animoID]
#query can be done either json or atom
base_url='http://search.twitter.com/search.json?q='+query_dict[animoID]+'&rpp=30&locale=es&result_type=recent'
f = 0
try:
f = urllib2.urlopen(base_url)
except urllib2.URLError, (err):
print "URL error(%s)" % (err)
if (f != 0):
a = json.loads(f.read())
#debug
#todo keep the msg if somethings happens
b = json.dumps(a, sort_keys=True, indent=4)
first_tw_time = a['results'][0]['created_at']
last_tw_time= a['results'][29]['created_at']
tstart = time_string_to_stamp(first_tw_time)
tend = time_string_to_stamp(last_tw_time)
tps = 30 / (tstart - tend)
else:
print 'We shouldnt be here, as this is bad'
tps= c.all_tpm[animoID] / 60 #If we cannot get value from http we keep the old one
#returning the tweets per second and all the message just in case we have an alert
return tps,b

Una vez registrados los tweets por minuto ya podíamos empezar a estudiar las emociones, mi idea se centraba en saber que ocurría en el mundo en tiempo real, mediante la fase de estudio observaba picos de alguna emoción que hacía que me picase la curiosidad. ¿Qué estaba pasando en twitter?
Pero no tenía tiempo para saberlo, el pasado era pasado, los "timestamps" demasiado ajustados para una posterior búsqueda y el tiempo que requería esa búsqueda no lo tenía y no lo quería.

Tampoco me apetecía que múltiples alertas cortas en el tiempo estuvieran generando alarmas continuamente. Así que necesitábamos aplicar algunas fórmulas matemáticas o de estadística que dieran el peso adecuado a las emociones.

Nos interesa saber qué es una emoción normal en twitter y que es lo que supone un cambio en ese comportamiento, para ello, y viendo resultados de otros estudios, me decidí por utilizar moving averages. Aplicando un factor de suavizado a las medidas registradas, concretamente un factor exponencial que diera una importancia exponencial en el tiempo para las medidas pasadas.

S_{t} = \alpha \times Y_{t} + (1-\alpha) \times S_{t-1}



El ánimo mundial normal irá evolucionando, al igual que la temperatura global del planeta puede cambiar, el ánimo lo hace de la misma manera, como por ejemplo las épocas de glaciación influirán de una manera diferente en la temperatura media o en la temperatura inmediata.

Así en el código diferenciaremos entre emociones, ánimo y temperamento mundial. Las emociones son inmediatas y están basadas directamente en la cantidad de tweets por minuto para un instante determinado. El ánimo se extiende más en el tiempo y finalmente el temperamento del mundo abarca toda la historia del mundo, desde que el código se lanzó a ejecutar, claro.

#aplicamos exponential moving averages
self.animo_mundial_avg[animoID] = self.animo_mundial_avg[animoID] * (1 - a) + tpm * a
#debug print 'timestamp: '+str(self.timestamp)+' animo at T '+str(self.animo_mundial_avg)
for i in range(NUM_TIPOS_ANIMO):
self.ratios_temperamento[i] = self.ratios_temperamento[i] * (1 - a) + self.ratios_animo_mundial[i] * a

El procesado de las emociones se realizaría por un lado y la muestra de los resultados por otro. Para la parte de resultados utilicé una placa de prototipado Open Source, Arduino.
El circuito montado sobre la placa es muy simple, es el mismo que sirve para encender y apagar un led, en este caso con las modificaciones oportunas de software para adaptarlo a la medida de las emociones.

Después de compilar Arduino desde sus fuentes sobre un Ubuntu Lucid 64 bits, me puse manos a la obra con el código que se encargaría de recoger los resultados de procesado Python para mostrarlos con el led RGB.
El led mostraría en tiempo real las reacciones de las emociones de la gente siguiendo el siguiente patrón de colores:

enum COLORID {
ROSA = 0,
AMARILLO,
NARANJA,
ROJO,
VERDE,
AZUL,
BLANCO,
NUM_COLORS,
};

Amor rosa, Alegría amarillo, Naranja sorpresa, Rojo ira, Verde envidia, Azul tristeza y Blanco miedo. Modificando suavemente los cambios de colores mediante una función de fading.

Necesitaba mostrar de alguna manera visual cuándo se generaba algún evento importante y para ello introduje una función de flash, de manera que cuando algo extraordinario ocurre el código Python le pasa via serie a Arduino un número completamente diferente a una emoción, le pasa un 9 (las emociones se identifican de 0 a 6), y entonces Arduino se encarga de realizar el apagado y encendido del led mostrando el color de la emoción que ha generado la alerta. Para ello utilizo las siguientes líneas:

for (int numflashes = 5; numflashes >= 0; numflashes-=1){
analogWrite(RED_LED_PIN,0);
analogWrite(GREEN_LED_PIN,0);
analogWrite(BLUE_LED_PIN,0);
delay(1000);
analogWrite(RED_LED_PIN,Colors[lastcolorID].r);
analogWrite(GREEN_LED_PIN,Colors[lastcolorID].g);
analogWrite(BLUE_LED_PIN,Colors[lastcolorID].b);
delay(1000);
}

Antes comentaba que Python le pasaba vía serie las alertas a Arduino, en efecto, vía USB, siendo este usb el cable de alimentación y datos para Arduino. Por ahora es necesario tener una ventana de consola abierta para la interfaz serie que usemos, utilizando ino (cli de arduino) podríamos abrir una sesión de screen ejecutando: ion -p /dev/ttyACM1 por ejemplo.

La portabilidad de nuestra lámpara de emociones llega con la Fonera o un router Acton, mediante el flasheado de OpenWrt podemos dar una nueva vida a la Fonera si es que ya no la utilizamos. La fonera tiene una memoria limitada, pero nos permite instalar python-mini mediante el gestor de paquetes opkg y con unas pinceladas al código y añadiendo una pilas a modo de batería, ya estaríamos preparados para ir a la calle parando a las personas y explicándoles que has conseguido almacenar en una lámpara todo el ánimo mundial.

O bien, como también explican otros, utilizarla en tu mesita de noche para dormirte cuando el nivel de Ira haya disminuido. O quien sabe, igual hacerte rico comprando y vendiendo acciones teniendo en cuenta el ánimo del mundo.

Por el momento yo la utilizaré un tiempo para registrar los grandes cambios de ánimo y temperamento, mediante el envío de un email cuando se produzcan los picos indicados anteriormente. Y lo mismo retroalimentaré mi cuenta de twitter, con las emociones recogidas en un intento de dar conciencia al mundo de si mismo.

Por supuesto me encanta tener la lámpara cerca del ordenador mientras trabajo o escucho música y quedarme pensando cuando esa pequeña luz cambia de color por las emociones de otros, no sé, es una extraña sensación.





Código fuente del proyecto:
Referencias:
Documentación Arduino: http://www.arduino.cc/en/Main/Docs
Gnuplot: http://www.gnuplot.info/

Actualizaciones:
Dic. 2013 - Añadido soporte para la API 1.1 de twitter search