Ponyo en el acantilado Junio 5, 2009
Posteado por Edorka en : Cine , hay 2 comentariosDefinitivamente la última obra de los estudios Ghibli supone un paso en la recuperación de la fama del después del fiasco de “Historias de Terramar”.
Es una película definitivamente infantil: el protagonista es un niño de 5 años y el argumento es bastante simple. Podríamos situarla sin problemas entre “Niki, aprendiz de bruja” y “El viaje de Chihiro”. Desde luego es muy diferente de otras historias muchos más dramáticas y violentas como “La princesa Mononoke”.
La historia transcurre en un pueblo portuario, donde Sasuke, un chaval muy espabilado lleva una vida más o menos tranquila hasta que encuentra a Ponyo, un pez rojo que resulta no ser un pez rojo cualquiera, pero de este punto no quiero desvelar nada.
Como en cada película el director quiere transmitir un mensaje, que en el caso de Ponyo es claramente medioambiental.
El dibujo es tremendo, como nos tiene acostumbrados Miyazaki. Como novedad cabe destacar que en algunas escenas han introducido algo de render (bastante discreto) seguramente como parte de la colaboración que estudios Gonzo ha hecho en la película.
La banda sonora como siempre compuesta por Joe Hisaishi acompaña perfectamente cada escena de la película y enfatiza los momentos más impresionantes.
Recomendaría verla en versión original, pero como seguramente quienes vayan a verla (si aún la tenéis en algún cine en vuestra ciudad) lo hagáis acompañados de gente menuda poco amigos de los subtítulos tendréis que confiar en la habilidad de los dobladores españoles (menos acostumbrados y comprometidos que los dobladores japoneses al trabajo de la animación).
Desde luego creo que este será de los títulos que compraré sin pensármelo 2 veces en DVD.
Si se me permite calificar la película yo daría un 8, aunque no es mi favorita del genio desde luego se ha ganado un hueco en mi estantería.
Práctica 1 y 3 Febrero 1, 2009
Posteado por Edorka en : Robótica , hay 2 comentariosEsta vez me ha tocado diseñar y programar un robot con la dificultad añadida de que debe superar los objetivos de la práctica 1 y la práctica 3, bastante diferentes entre ellas.
Me propuse hacerlo con la misma configuración física que aparece en las fotos y que ha resultado bastante desastrosa ya que pesaba demasiado para cumplir con los objetivos de la práctica 3 (mantener equilibrio dinámico).
Práctica 1: Esquiva obstáculos
Para este el robot tan solo tenía que utilizar el sensor de ultra sonidos y reaccionar ante objetos que estuvieran a menos de 30cms, girando a la derecha y volviendo a avanzar
. También tenia en carril de los sensores montado de tal manera que el sensor delantero se activara en caso de impacto frontal, ante esto el robot debía retroceder, girar y proseguir con la marcha.
Al tener el sensor de ultrasonidos inclinado hacia arriba lo activarán obstáculos grandes (paredes, puertas, etc) sin embargo queda un hueco importante para que el robot “tope” con un objeto.
El carril donde están montados los sensores ha sido diseñado para que el sensor de presión al que esta conectado pueda ser activado tanto por impactos frontales como inferiores.
Aquí esta el código. Copiadlo al directorio samples de lejos y ejecutad ant.
Práctica 2: Segway
Para esta práctica y empleando un único sensor de luz debemos programar y diseñar el robot para que se mantenga en equilibrio el mayor tiempo posible.
Algunas consideraciones sobre el diseño:
- El robot dispone de dos sensores de presión (uno delante y otro detrás) para detectar que ha caido y en consecuencia detener el movimiento y evitar daños.
- Estos sensores se utilizan activamente en la fase de calibrado para asegurarse de que el usuario lo esta realizando en el orden correcto (dejarlo caer hacia atras, luego hacia delante y activarlo en equilibrio)
Para controlar la fuerza de los motores se utiliza un controlador PID, en la pagina de la wikipedia se pueden encontrar mas detalles, aquí tan solo se comentará lo relativo a la implementación del Segway.
En cada ciclo de control el controlador obtiene una lectura del sensor de luz, esta lectura será 0 si el robot se haya en el punto de equilibrio, los valores positivos y negativos indicarán si el robot se esta inclinando hacia delante (si es positivo) o hacia atrás (si es negativo)
En consecuencia el robot debe responder para recuperar la posicion de equilibrio con una acción correctiva, en el caso del segway consistirá en desplazarse proporcionalmente hacia el punto que le permita recuperar el equilibrio.
Para calcular la velocidad con la que deben responder los motores y su direccion participan 3 constantes que suelen ser diferentes de un robot a otro ya que la inercia del robot y la potencia real de los motores interviene en la respuesta.
- KP es la respuesta proporcional, normalmente si es muy elevada puede hacer que el robot “sobre-actue” y se caiga al lado contrario, en las pruebas que he hecho sin embargo nunca ha llegado a conseguir neutralizar una inclinacion media.
- KI es la constante de integracion y multiplica las diferencias con el punto de equilibrio que se han ido sumando (hasta llegar a una constante máxima)
- KD, la constante de derivacion multiplica la diferencia de los dos ultimos errores obtenidos.
No todas las constantes tienen porque ser positivas, dependerá de que papel jueguen en el refuerzo o compensación de la respuesta para conseguir que el robot reaccione con la fuerza necesaria pero sin provocar que caiga en el sentido contrario.
El código está disponible aquí
Fallos y puntos a mejorar:
Quien mucho abarca poco aprieta, el motivo principal del fallo catastrofico del robot a la hora de mantener el equilibrio ha sido intentar construir una configuracion que resolviera simultaneamente las 2 prácticas pendientes para entregar el mismo día. El problema de esta configuración hibrida radicaba en el peso, que hacia imposible a los motores moverse con el suficiente impulso como para volver a ganar el centro de equilibrio.
por otra parte el entorno del laboratorio devolvía lecturas bastante cerradas con lo que el robot no aceptaba la lectura que le daba como punto medio (al no ser menor que la lectura máxima y mayor que la mínima)
Práctica 2: Conclusiones Diciembre 17, 2008
Posteado por Edorka en : Robótica , poner un comentarioTenacitas ya se ha enfrentado con un relativo éxito a su propósito en la vida.
He llegado a las siguientes conclusiones:
Las pinzas son un buen sistema para capturar objetos de ciertas formas, sin embargo con el diseño actual de las pinzas se daba la posibilidad de que objetos que tuvieran demasiado rozamiento no se “deslizaran” hasta tocar el sensor.- La mejor manera de detectar si las pinzas se han cerrado consiste en vigilar el angulo actual del motor que las mueve y así evitar que se siga moviendo cuando el mecanismo está bloqueado (esto es muy importante para evitar el deterioro de las piezas. Si a alguien le interesa como esta realizado el mecanismo de las pinzas dedicaré un post entero al tema. De momento dejo un vídeo mostrando el sistema de captura en acción.tenacitas-capturando
- Los sensores de lego suelen ser bastante poco precisos y susceptibles del ruido, esto es muy común y precisamente por ello debí pre-veer algún mecanismo para realizar filtros est
adísticos sobre los sensores de ultrasonidos y sobre todo del sensor de luz. - Aconteció un epic FAIL en un par de ocasiones cuando el robot alcanzaba la cinta aislante que marca el fin del mapa, en este caso el robot gira 100 grados a su izquierda y prosigue. Desgraciadamente esta política no es efectiva el 100% de las veces, ya que Tenacitas tiene el sensor de luz 6′5cms adelantado respecto del eje de movimiento, con lo que existe la posibilidad de que el sensor quede por delante del limite y que con ello el robot prosiga más allá de los límites del tablero. La solución a este problema consiste en retroceder unos centímetros antes de iniciar el giro.
- Debido a la persistente cojera de Tenacitas la odometría no era suficiente para establecer la posición del robot, este siempre se desplaza unos 5 grados a la derecha en cada desplazamiento, con lo que la incertidumbre crece tanto que se hace imposible hacer estimaciones sobre la posición del robot en el mapa, aunque sería interesante ver hasta que punto se pueden ir contabilizando estas taras permanentes y que el robot cuente con ellas para establecer su posición.

- Estoy especialmente satisfecho con el sistema para la localización de latas, este consiste en qu e el robot avanza 30cms (la distancia máxima en que el sensor de ultrasonidos es preciso) y rota 45grados a un lado, 90 al contrario y vuelve a mirar al rumbo original, interrumpiéndose el proceso si aparece un objeto susceptible de ser una lata. De este modo se maximiza el aprovechamiento del sensor y nos aseguraremos de que no sea necesario pasar dos veces por la misma zona.
- Si el sistema ha localizado una lata y esta desaparece del sensor, la buscara repitiendo un escaneo de 110grados (todas estas constantes son configurables), si al finalizarlo no ha conseguido encontrar la lata, la dará por perdida y proseguirá con la búsqueda de otra lata.
Práctica 2: Recoge latas Diciembre 14, 2008
Posteado por Edorka en : Robótica , poner un comentarioMis más cercanos habrán notado cierta obsesión estos últimos días con cierto artilugio que me ha sido encomendado para convertirlo en un estupendo caza latas.
Está pensado para moverse a través de un tablero que tiene una serie de limites marcados con cinta negra, detectar y capturar las latas para depositarlas en un recuadro de colores en el centro del tablero.
Lo mejor de todo, tiene que estar terminado y probado para el martes, ya contaré que tal.
llevar o poner Noviembre 25, 2008
Posteado por Edorka en : Uncategorized , hay 1 comentarioAprovechando el revuelo que está levantando el tema de los crucifijos en las aulas pongo mi granito de arena en un post cortito.
De los creyentes que conozco (entre los que hay excelentes, buenos, regulares, malos e inclusive malvados) creo que puedo decir que la mayoría debería estar más preocupado o preocupada (y de hecho no se les olvida) por llevarlo encima que por andar colgándolo en paredes ajenas.
Así pues opino que el crucifijo como otros tantos símbolos de creencia personal, e intransferible, deben llevarse, en el pecho cerca del corazón para subrayar su caracter íntimo. Pero lo de ponerlo en las paredes es además de poner, imponer.
Formas de hacer las cosas (y de no hacerlas) Noviembre 18, 2008
Posteado por Edorka en : Uncategorized , poner un comentarioLos Jueves suelen ser días interesantes, principalmente porque indican que estás a mitad de la semana y que ademas los días que restan son los más interesantes. Para mi ademas tenían otro añadido que era poder leer la columna de Cristina Fallarás en el ADN.
Pero ahora me entero en su blog que además de quedarme sin su columna los motivos son cuanto menos sospechosos.
En fin, desde mi pequeña tribuna mando al ADN a tomar por …. (rellenar la linea de puntos). total lo unico bueno que les quedan son los sudokus y esos me los puedo conseguir yo.
Separar o no separar Noviembre 1, 2008
Posteado por Edorka en : Uncategorized , hay 1 comentarioGeosincrona.com y antes ultimaorbita.com nacieron con una voluntad principalmente técnica por mi parte. Con el tiempo fui subiendo algunos dibujos y algún comentario personal.
Si tuviera una cita semanal como morghost con su tira cómica Fantasmitar S.L. no se si dibujaria más, pero probablemente utilizaría más el escaner.
Las ideas que estoy barajando son:
- Dejar geosincrona.com unicamente para asuntos técnicos y montar otro blog para asuntos personales y dibujos (incluso habilitar votaciones)
- Dejar geosincrona.com solo para asuntos personales y publicar los articulos técnicos en otra parte.
- Apañar todo esto utilizando de manera inteligente las categorías.
En fin agradeceré sugerencias en los comentarios.
Módulos en C para Python II: Objetos Octubre 9, 2008
Posteado por Edorka en : C, Programacion, Python , poner un comentarioEn algunas ocasiones implementando extensiones para Python se hace necesario proveer de objetos con sus respectivos atributos en vez de funciones accesibles directamente a través del módulo. Para este ejemplo se emplearán figuras geométricas. Lo primero es crear las estructuras de datos involucradas.
#include
staticforward PyTypeObject figures_TriangleType;
typedef struct {
PyObject_HEAD
float a,b,c;
char *name;
} figures_TriangleObject;
La sentencia staticforward permitirá acceder a la estructura del tipo Python de Triangle aunque no se haya declarado, aunque no sea muy ortodoxo es necesario ya que no se dispondrá de las funciones y datos para componerlas hasta las últimas lineas del módulo.
No se debe confundir TriangleType con TriangleObject, la primera será la estructura que utilizará Python para saber que métodos y atributos pertenecen al objeto y en definitiva hacer que este se comporte como tal, en cambio es en la estructura TriangleObject donde se encuentran los datos que se instanciarán con el propio objeto durante la ejecución.
A continuación se define la función de construcción del objeto, reservando memoria para la estructura de datos que le corresponde, la función encargada de ello es tp_alloc() que está incluida en la estructura del TypeObject. Una vez creado el objeto se devuelve.
static PyObject*
figures_triangle_new(PyTypeObject *type, PyObject *args){
figures_TriangleObject *self;
self = (figures_TriangleObject*)type->tp_alloc(type,0);
return (PyObject *) self;
}
Es en la función de inicialización cuando se procede al análisis de los parámetros (arg) facilitados. En este caso se espera que se faciliten una cadena seguida de tres decimales, el formato que utiliza esta función es muy parecido al que emplean otras como printf().
static int
figures_triangle_init(PyObject *self, PyObject *args) {
char *name;
float a,b,c;
figures_TriangleObject* triangle;
if (!PyArg_ParseTuple(args, "sfff", &name, &a,&b,&c))
{
PyErr_SetString( PyExc_TypeError,
"Parameters: name string, a, b and c lenght"
);
return -1;
}
if (self != NULL) {
triangle = (figures_TriangleObject*) self;
} else {
triangle = PyObject_New(figures_TriangleObject, &figures_TriangleType);
}
triangle->a = a ;
triangle->b = b ;
triangle->c = c ;
triangle->name = name;
return 0 ;
}
Debe tenerse en cuenta de que si el patrón esperado para los parámetros no coincide con los que se facilitan se emitirá un mensaje de error informando del suceso. Esta función devolverá 0 si el proceso de la inicialización ha funcionado correctamente, en caso contrario un entero negativo.
También es posible que el puntero a self sea nulo, en tal caso esta función será la encargada de invocar a la rutina que reserve memoria, en cualquier caso se proseguirá asignando los valores recibidos en los parámetros a las variables del objeto.
La función anterior es invocada desde una función de creación, que controla que esta se ha realizado correctamente y en caso contrario devolver un objeto nulo (None).
static PyObject*
figures_triangle_create(PyObject *self, PyObject *args){
figures_TriangleObject *new;
figures_TriangleType.tp_new = PyType_GenericNew;
PyType_Ready(&figures_TriangleType);
new = PyObject_New(figures_TriangleObject, &figures_TriangleType);
if (figures_triangle_init( (PyObject *)new , args) == 0)
return (PyObject *)new;
else
return Py_None;
}
Para crear el objeto debe implementarse la función correspondiente, será esta la que se llamará cuando se haga referencia en Python a figures.Triangle().
La complejidad del destructor de datos dependerá de la estructura propia del objeto, para el caso del triangulo es muy simple.
static void
figures_triangle_dealloc(figures_TriangleObject* self)
{
self->ob_type->tp_free((PyObject*)self);
}
La estructura TriangleObject incorpora como todo PyObject punteros a funciones para gestionarlos como objetos, una de estas es precisamente la rutina para dejar de reservar la memoria ocupada por la estructura del PyObject.
El primer método de este objeto servirá para calcular el área, debe tenerse en cuenta que Python debe obtener al final de la función un objeto válido, para eso se emplea la función Py_BuildValue().
static PyObject*
figures_triangle_area(figures_TriangleObject *self, PyObject *value, void *closure){
float area;
area = self->a * self->b / 2;
return (Py_BuildValue("f",area));
}
Py_BuildValue() utiliza las mismas normas de formato que PyArg_ParseTuple().
Para acceder a los atributos del objeto deben programarse las respectivas funciones get() y set(), a se implementa método get() para recuperar la longitud del lado A del triángulo.
static PyObject*
figures_triangle_get_a(figures_TriangleObject *self, void* p )
{
return Py_BuildValue("f", self->a);
}
El método set() es algo mas complicado ya que tenemos que debe confirmase que facilita un parámetro y que además este es del tipo float.
static int
figures_triangle_set_a(figures_TriangleObject *self, PyObject *value, void *closure)
{
float a;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError,
"Cannot delete the first attribute");
return -1;
}
if (PyArg_Parse(value,"f", &a )) self->a = a ;
else {
PyErr_SetString(PyExc_TypeError,
"The first attribute value must be float");
return -1;
}
Py_INCREF(value);
return 0;
}
Las descripciones de getter y setter son necesarias para que python sepa que atributos puede manejar y a que métodos corresponde. Cada registro debe incluir:
- El nombre del atributo en una cadena.
- Un puntero a la función para el método get a al que debe preceder un cast (getter).
- Un puntero a la función para el método set a al que debe preceder un cast (setter).
- Una cadena con una breve descripción del atributo.
- Un puntero a datos opcionales que se hará llegar a las anteriores funciones a través del parámetro closure, en la mayoría de los casos será nulo.
El último registro de esta estructura contendrá un único valor NULL.
Para más detalles sobre las estructuras que emplean las extensiones Python en C existe documentación completa.
static PyGetSetDef figures_triangle_getset[] = {
{ "a", (getter)figures_triangle_get_a,
(setter)figures_triangle_set_a,
"a lenght", NULL },
{ NULL }
};
También deben declararse los métodos en una estructura PyMethodDef que al igual que la responsable de los atributos se incluirá en la estructura del tipo Python para Triangle. Cada método debe tener un registro compuesto por:
- El nombre del atributo en una cadena.
- Un puntero a la función haciendo un cast como (PyCFunction).
- Un flag que indicará si deben proporcionarse parámetros al método, la alternativa es METH_VARARGS.
- Por último una breve descripción del método.
static PyMethodDef figures_triangle_methods[] = {
{ "area", (PyCFunction)figures_triangle_area,
METH_NOARGS , "calculates area"},
{NULL, NULL, 0, NULL}
};
De la misma manera se declaran los métodos para el propio módulo, en este ejemplo se proporcionará la función constructora de Triangle.
static PyMethodDef figures_methods[] = {
{"Triangle", (PyCFunction)figures_triangle_create, METH_VARARGS,
"Create a new Triangle object."},
{NULL, NULL, 0, NULL}
};
Para rellenar la estructura PyTypeObject deben utilizarse, al igual que en las estructuras anteriores, casts para las funciones con el propósito de evitar warnings.
static PyTypeObject figures_TriangleType = {
PyObject_HEAD_INIT(NULL)
0,
"figures.Triangle",
sizeof(figures_TriangleObject),
0,
(destructor)figures_triangle_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0,0,0,
0,0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
"Triangle objects",
0,0,0,0,0,0,
figures_triangle_methods,
0,
figures_triangle_getset,
0,0,0,0,0,
(initproc)figures_triangle_init,
0,
(newfunc)figures_triangle_new,
};
DL_EXPORT(void)
Por último el inicializador del módulo indicará que estructura contiene los métodos de este.
initfigures(void){
PyObject* m;
figures_TriangleType.ob_type = &PyType_Type;
m = Py_InitModule("figures", figures_methods);
}
Para compilar el módulo debe ejecutarse el siguiente script Python:
from distutils.core import setup, Extension
setup(name = "figures", version = "1.0",
ext_modules = [Extension("figures", ["figures.c"],
library_dirs = [ './' ] ,
)]
)
Cabe recordar que si se necesita incluir librerias opcionales puede añadirse a los parámetros de Extension() la tupla:
extra_objects = ["libreria.a"]
Para utilizar la libreria tendreis que copiar el fichero figures.so desde el directorio que distutils ha creado dentro de build al directorio actual.
Módulos en C para Python Octubre 6, 2008
Posteado por Edorka en : C, Programacion, Python , poner un comentarioTraducción de Anatomy of a Python C Module
Escribir módulos C en Python es relativamente fácil. La razón principal para hacer esto es incrementar el rendimiento de un código en Python. A continuación se demostrará como implementar la siguiente función de Python en C. Esta función puede encontrarse aquí:
def fib2(n): # return Fibonacci series up to n
"""Return a list containing the Fibonacci series up to n."""
result = []
a, b = 0, 1
while b < n:
result.append(b) # see below
a, b = b, a+b
return result
Cabe destacar que esta no es una función particularmente lenta -en realidad es bastante rápida- Simplemente tiene una serie de aspectos interesantes para implementar un modulo C en Python, como la creación de una lista de Python en C. El ejemplo de la creación de un modulo C para Python no es tan amplio como podría llegar a serlo, si todo va bien la implementación de la secuencia Fibonacci en C resultará un poco más explicativa.
Para empezar siempre se incluye Python.h:
#include Python.h;
Lo siguiente será crear la función fib. Primero definimos la función como un Python Object, al que se le pasaran argumentos:
static PyObject *
fib(PyObject *self, PyObject *args)
{
Después una vez en el cuerpo de la función inicializamos algunas variables:
int a = 0, b = 1, c, n;
Entonces será cuando se realice el analisis de los parámetros proporcionados a la función. Para esto se utilizar PyArg_ParseTuple. Puede encontrar mas documentación en Parsing arguments and building values, que dará una visión general sobre como analizar diferentes tipos de parámetros. De todos modos en este ejemplo solo se acepta un único valor entero. Si esto no funciona se devolverá NULL.
if (!PyArg_ParseTuple(args, "i", &n))
return NULL;
Después se instanciará una nueva lista Python, utilizando PyList_New, que aceptará un número entero como longitud de la lista. Ya que se desconoce fual será la longitud se empezará con cero.
PyObject *list = PyList_New(0);
A continuación están las entrañas del verdadero cálculo. Se debe prestar atención a la sentencia PyList_Append(list, PyInt_FromLong(b));, ya que es donde añadimos un nuevo item a la lista. PyList_Append es analogo al metodo de Python list.append(). Se utiliza PyInt_FromLong para crear un objeto Python con un entero dentro del bucle.
while(b < n){
PyList_Append(list, PyInt_FromLong(b));
c = a+b;
a = b;
b = c;
}
Y entonces se devuelve la lista:
return list;
}
Esto conforma las entrañas de la funcion, pero como integrar esto en Python como un módulo? Primero debe crearse un objeto PythonMethodDef con las funciones que quieren incorporarse al modulo. Ya que solo tenemos una función solo tenemos que añadir una definición, como esta:
PyMethodDef methods[] = {
{"fib", fib, METH_VARARGS, "Returns a fibonacci sequence as a list"},
{NULL, NULL, 0, NULL}
};
El último paso es inicializar el módulo. Para entender que ocurre en esta parte debe leerse esta página que contiene una explicación completa del proceso.
PyMODINIT_FUNC
initfib()
{
(void) Py_InitModule("fib", methods);
}
Ahora que el modulo en C para Python esta completo, tiene que compilarse. La forma más fácil de hacerlo es utilizar el módulo distutils. Creamos setup.py como sigue:
from distutils.core import setup, Extension
setup(name = "Fib",
version = "1.0",
ext_modules = [Extension("fib", ["fib.c"])])
Con eso se informa a distutils que nuestro modulo esta ubicado en fib.c. Ahora se ejecuta:
$ python setup.py build
$ python setup.py install
Y ya está instalado, para utilizarlo debemos importar el módulo y utilizar la función:
import fib
fib.fib(123)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Una secuencia fibonacci es fácil de calcular, y hacerlo en C es un ejercicio para mostrar como se pueden implementar funciones y módulos en C. Aun así, y aunque sea facil de calcular, la versión en C es cuatro veces más rápida que su equivalente en Python. Este simple ejemplo debería mostrar como de fácil -y útil- es implementar extensiones en C
Hay mucho mas sobre este procedimiento en Extending and Embedding the Python Interpreter que recomiendo leer.
Defensa del proyecto de fin de carrera Septiembre 17, 2008
Posteado por Edorka en : Arte, Programacion, Python , poner un comentarioEl Viernes 18 de Septiembre a las 13:00h defiendo en la sala 103 del Departamental II de la Universidad Rey Juan Carlos (Campus de Móstoles) mi proyecto de fin de carrera.
A grandes rasgos BLAS es una plataforma en python que pretende estructurar y agilizar la elaboración de servicios a nivel de aplicación, tanto sobre TCP como UDP.
El proyecto está publicado bajo la GPLv3 en http://code.google.com/p/blas/
A esto debería añadir que tengo un gripazo muy serio y que mi aspecto será bastante lamentable ese día.




