Emergencia en la mar

 Nautica  Comments Off
Sep 122015
 

El Airam Cinco es un velero de 33 pies del armador Mario Ruiz de Villa, con base en Santander, y que participa habitualmente en las regatas de crucero que se celebran en el Cantábrico. El pasado día 26 de agosto hacía el viaje de regreso a Santander tras una exitosa participación en las regatas El Gaitero en Asturias y Rías Baixas en Galicia. Ese día estaba previsto viajar desde Gijón hasta San Vicente de la Barquera, 60 millas, unas diez horas de navegación.

A las 9:30 de la mañana salimos del puerto de Gijón. El repostaje y otros preparativos nos retrasaron una hora respecto del horario inicialmente previsto para la travesía. San Vicente de la Barquera es un puerto pequeño que con bajamar tiene muy poco calado. La bajamar de ese día era a las nueve de la noche. La previsión meteorológica era buena, el viento del este-nordeste flojo, de unos ocho nudos.

La tripulación la formábamos Mario Ruiz de Villa, armador del Airam y excelente marino y persona, y yo mismo como marinero. Además viajaban a bordo cuatro pasajeros familiares de Mario, dos hombres y dos mujeres, sin conocimientos de náutica.

El viaje transcurría con normalidad. Comimos bocadillos y tuvimos ocasión de charlar y conocernos un poco. Íbamos navegando a motor y con la mayor izada. A ratos podíamos utilizar un poco el foque, aunque el viento estaba tan de proa que nos obligaba a desviarnos de la línea recta hacia nuestro destino, por lo que dada la prisa que teníamos por la marea en San Vicente, navegábamos más tiempo sin foque que con él.

Posición del Airam en el momento del incidente

Posición del Airam en el momento del incidente

Hacia las 14:30, hora oficial española, navegando a la altura de Ribadesella y estando a unas siete millas de la costa, observamos que salía humo de la cabina. Pusimos el motor a ralentí y bajamos a ver qué pasaba. Lo que vimos nos dejó helados: la cabina y, sobre todo, el camarote de popa estaban llenos de humo. Mario abrió una de las tapas de la sentina y descubrió que el agua salía a borbotones. Me mandó calar el motor y dar aviso por la emisora a Salvamento Marítimo. ‘Salvamento marítimo, salvamento marítimo: aquí Airam‘. Noté que me temblaban un poco las manos. No hicimos MAYDAY, simplemente informamos de que teníamos una vía de agua y que habíamos parado el motor por el humo. Mientras yo me entendía con Salvamento Marítimo de Gijón, Mario fue desmontando los paneles que separan el compartimento del motor del camarote de popa, para poder averiguar qué estaba pasando.

Lo primero que pensó Mario fue en la cola del motor, que había tenido una reparación días antes de las regatas. La cantidad de agua que habíamos embalsado era muy grande. Pusimos a funcionar la bomba eléctrica de achique de la sentina. El compartimento del motor también estaba inundado y se habían producido algunos cortocircuitos que, entre otras cosas, nos dejaron sin funcionar el GPS de la embarcación. Esto también originó la primera anécdota durante nuestras comunicaciones con salvamento. Como el GPS de bitácora había dejado de funcionar debido a los cortocircuitos, utilizamos un programa del smartphone para comunicar las coordenadas a salvamento. Salvamento Marítimo movilizó el Helimer 207, con una bomba de achique, y la Salvamar Rigel. Nos informaron de que la Salvamar tardaría varias horas en llegar, dada la distancia a la que nos encontrábamos ya de Gijón. El programa es uno hecho por mí, que para facilitarme los cálculos posteriores que hago con los datos que recojo, da la posición en grados y decimales de grados, en lugar de utilizar grados, minutos y decimales de minutos, como está prescrito en comunicaciones náuticas. Por esto, la posición que yo leía en la emisora como ‘cero cero cinco punto cero ocho uno‘ y de manera similar para la latitud, fue interpretada por Salvamento Marítimo como grados y minutos, lo que motivó que, un rato después, el Helimer 207 avisara por la emisora de que en la posición indicada no había ninguna embarcación. Aclaramos el malentendido y seguimos con la inspección de la avería.

El Helimer 207 sobrevolando el Airam

El Helimer 207 sobrevolando el Airam

El agua acumulada en el compartimento del motor nos hizo pensar que podía haber alguna rotura en el conducto de refrigeración que estuviera vertiendo agua al interior de la embarcación. Mario me mandó hacer algunas pruebas: ‘Para motor, arranca motor‘ y comprobamos que, efectivamente, el agua de refrigeración no salía por el conducto de desagüe. Inmediatamente Mario comenzó a seguir el conducto a través de los distintos tambuchos que atraviesa hasta que dio con el punto en que una soldadura se había roto, lo que causaba que todo el agua se vertiera en la sentina a través del tambucho. La búsqueda hubo que hacerla sin linterna pues, pese a que llevamos una docena de ellas en el barco, con los nervios y el desorden tras las regatas, no fuimos capaces de encontrar ninguna que funcionase.

Tubo de refrigeración vertiendo a través de la bañera

Tubo de refrigeración vertiendo a través de la bañera

Localizado el problema, hicimos una reparación de fortuna sacando el conducto de refrigeración a través de la bañera, para verter por popa el agua de refrigeración. Hicimos unas pruebas de funcionamiento del motor con el tubo desaguando de esta manera y tras comprobar que funcionaba, procedimos a informar a Salvamento Marítimo de que habíamos controlado la vía de agua y cancelábamos la solicitud de remolque. La Salvamar Rigel, canceló la salida. No así el Helimer 207 que amablemente acudió hasta nuestra posición. Yo no llegué a verlo, pues cuando nos sobrevoló me encontraba abajo en la cabina comunicándome por la emisora con Gijón y con el propio helicóptero, al que informamos de que habíamos localizado la avería e íbamos a intentar hacer una reparación de fortuna y seguir por nuestros medios hasta San Vicente.

Flujo de agua procedente de la refrigeración

Flujo de agua procedente de la refrigeración

Seguimos achicando agua con la bomba eléctrica y la manual situada en la bañera. Había una reparación más que hacer, pues el orificio por el que habitualmente desagua la refrigeración quedaba expuesto muy cerca de la línea de flotación, haciendo de vía de agua. El orificio era de unos 10 centímetros de diámetro y no disponíamos de ningún espiche de ese tamaño. Una vez más Mario demostró su pericia utilizando unos vasos de plástico para taponar la vía. Durante todo este rato habíamos estado en comunicación de manera intermitente con el resto de la tripulación habitual del Airam a través del grupo de chat que tenemos en whatsapp. Justo por este chat Pablo, @prl42, nos sugirió, con gran acierto, que macizáramos los vasos con servilletas de papel mojadas, lo que resultó ser una gran idea.

Reparación de fortuna taponando una vía de agua con vasos de plástico

Reparación de fortuna taponando una vía de agua con vasos de plástico

Con el motor en marcha, aunque a menos revoluciones de las habituales, dimos aviso definitivo a salvamento de que proseguíamos por nuestros medios. Nos dieron instrucciones de que les informásemos cuando recaláramos finalmente en puerto seguro para así cerrar definitivamente el incidente, ya que a veces las reparaciones de fortuna son eso, de fortuna, y a veces esta te abandona.

Ya en marcha hacia San Vicente, puse un tuit a @SalvamentoGob, agradeciéndoles su rápida intervención. y ahí surgió otra anécdota, pues @ManoloGallegos, uno de los pasajeros, me preguntó entonces: ‘Pero, ¿quién eres tú en tuiter, Santiago?‘. Le respondí que era @SantiagoHiguera y nos dimos cuenta de que nos seguíamos desde hacía mucho tiempo, y de que éramos compañeros de profesión, ya que ambos somos Ingenieros de Caminos.

tuitdesalvamento

Hacia las 9:30 de la noche iniciamos la entrada al puerto de San Vicente, en plena bajamar. Ahí también pasamos dificultades pues el calado era realmente escaso; llegamos a tener apenas 20 cm de agua bajo la quilla. Finalmente conseguimos amarrar sin mayor novedad y procedimos a informar a Salvamento Marítimo. En el puerto nos esperaban familiares y amigos, incluidos varios chiquillos que se montaron en el barco a jugar y trastear. A los pocos minutos de entrar en el barco, cada uno de ellos salió con una linterna. Las que habían encontrado revolviendo por ahí. Yo no pude evitar partirme de risa.



Santiago Higuera (12 de septiembre de 2015) (Corrección del texto original Esperanza Román Mendoza)

Jan 192015
 

Un problema que surge trabajando con los sensores de un smartphone es elegir el tipo de datos y de almacenamiento que permita gestionar la información generada por los dispositivos. Otro problema es sincronizar los datos procedentes de los distintos sensores.

Ya sea el acelerómetro, el clinómetro, o el GPS, lo que vamos obteniendo es una serie de valores reales asociados a un instante concreto de tiempo.

Los tiempos que nos entrega el ordenador están medidos en milisegundos y vienen representados por un valor entero, tipo long en Java, que representa los milisegundos transcurridos desde las 00:00 horas UTC del 1 de enero de 1970.

Los valores correspondientes a los sensores serán en general números reales, que en Java estarán representados por valores double. Así, en el caso de un acelerómetro, por ejemplo, tendremos por cada valor de tiempo una terna de valores double ax, ay, az.

El problema de la sincronización de los datos procedentes de distintos sensores es debido a la diferencia en el tiempo de actualización de los mismos. Si queremos sincronizar los datos procedentes de un sensor ‘lento’, como por ejemplo el GPS que actualiza cada segundo, con los datos de un sensor ‘rápido’, como el clinómetro, por ejemplo, que actualiza 15 o 20 veces por segundo, tendremos que aplicar algún tipo de filtro a los datos procedentes del sensor rápido para adaptarlos a los del sensor lento.

La clase TSerie de la librería GpxParser, [1], encapsula una lista de tiempos, List<Long>, de manera coordinada con una lista de arrays de doubles List<double[ ]>. Realmente, las listas de tiempos y de arrays de doubles están a su vez encapsuladas en sendas instancias de las clases TList y DoublesList de la propia librería.

Para crear una instancia de la clase TSerie utilizaremos su constructor sin parámetros, lo que dará lugar a la creación de una instancia de TSerie, con sus listas inicialmente vacías.

TSerie serie = new TSerie();

El método add() permite añadir elementos a la lista. Cada elemento constará de un valor long correspondiente al tiempo, y un array de doubles con los valores asociados. El primer elemento que añadamos a la lista establecerá la dimensión que deberán tener todos los arrays que añadamos a continuación:

TSerie serie = new TSerie();
long t = new Date().getTime();
double[] values = new double[] {1.0,2.0,-1.0};
serie.add(t, values);

El método add() realiza una serie de comprobaciones antes de añadir cada elemento. En el caso del tiempo, comprueba que el valor es mayor que cero y que es mayor que el último de los tiempos que se hayan añadido a la lista. En el caso del array de doubles, se comprueba que no es nulo y que su dimensión está de acuerdo con la de los arrays de la lista. Esa dimensión la establece el primer array que se añada a la lista. La clase TSerie ofrece un método utilitario canAdd() que permite comprobar si unos valores concretos se pueden añadir o no.

Para acceder a los valores almacenados en nuestro objeto de la clase TSerie disponemos de diversos métodos utilitarios:

  • size(): nos devuelve el número de elementos almacenados en la lista.
  • TList: La clase TSerie ofrece diversos métodos utilitarios para acceder a los métodos y propiedades de la serie de tiempos. Por ejemplo, se dispone de un método getTime() que permite acceder a los tiempos por su índice en la lista. Existen también dos métodos firstTime() y lastTime() que permiten obtener el primer o el último tiempo de la lista.
  • getValues(int): Se puede acceder a los valores doubles almacenados por su índice en la lista mediante el método getValues() pasando un entero como parámetro
  • getValues(long): Una segunda versión del método getValues(), que admite como parámetro un valor long correspondiente a un tiempo, devolverá los valores doubles correspondientes a ese tiempo, si el tiempo está comprendido en el intervalo de tiempos de la lista. Si el tiempo se corresponde exactamente con uno de los de la lista, se devolverá el array de doubles correspondientes. Si el tiempo no coincide exactamente con ninguno de la lista, se devolverá un array de doubles cuyos valores serán la interpolación lineal entre los valores correspondientes al tiempo inmediatamente anterior y el tiempo inmediatamente posterior.
  • getValues(long, StrategyOnValues): Este método admite, ademas de un tiempo, un segundo parámetro que es una instancia de la clase StrategyOnValues, que explicamos a continuación

El interface StrategyOnValues sólo tiene un método cuya signatura es:

double[] getValues(TSerie tserie, long t)

La clases que implementen el interface, devolverán el array de valores double que corresponden al tiempo t en la tserie. A la fecha de escribir este artículo existe una implementación concreta, la clase AverageStrategy. Esta clase recibe, en su constructor, un valor long llamado halfinterval. Los doubles devueltos por el método getValues son la media de los valores del sensor comprendidos en el intervalo de tiempo [t – halfinterval, t + halfinterval]. Esto es, cuando utilicemos esta estrategia para obtener los datos del sensor en un tiempo determinado t, obtendremos la media de los valores del sensor en un entorno de ese tiempo. El tiempo t no tiene por qué coincidir exactamente con uno de los de la lista.

En el ejemplo que se muestra a continuación, se define una TSerie con una serie de valores double para cada tiempo. Definimos a continuación una AverageStrategy con intervalo de tiempo t=100 ms. El resultado de la función getValues() utilizada a continuación nos devolverá un array de double double[] con los valores 2.0 y 4.66 correspondientes a la media de los valores entre t=200 – halfinterval= 100 ms y t=200 + halfinterval= 300 ms.

TSerie serie = new TSerie();
serie.add(new Long(0L), new double[] {1.0,1.0});
serie.add(new Long(100L), new double[] {1.0,1.0});
serie.add(new Long(200L), new double[] {2.0,4.0});
serie.add(new Long(300L), new double[] {3.0,9.0});
serie.add(new Long(400L), new double[] {4.0,16.0});

AverageStrategy strategy = new AverageStrategy(100L);
double[] values = serie.getValues(200L, strategy);
// Devuelve {2.0 4.66}


[1] https://github.com/shiguera/gpxparser



Santiago Higuera (15 de enero de 2015)

Feb 282014
 

Cuando arrancamos una aplicación Android, el sistema operativo crea un nuevo ‘hilo’ de procesamiento para la aplicación. En ese hilo, llamado hilo principal, es donde se ejecutan todas las operaciones del programa, incluidas las tareas relacionadas con la actualización del interfaz de usuario. Es por ello, que cuando realizamos una operación que consuma un tiempo apreciable, dejamos momentaneámente congelado el interfaz de usuario. Durante ese tiempo el interfaz no responderá a las acciones como pulsar botones, actualizaciones de pantalla, etc. En suma, es como si se quedara temporalmente colgada la aplicación. Ejemplos de este tipo de operaciones pueden ser el acceso a ficheros de la tarjeta de memoria, las consultas a través de internet o la inicialización de objetos complejos. Con el fin de que el interfaz de la aplicación atienda adecuadamente en todo momento a los eventos generados en el interfaz de usuario, es conveniente que dichas operaciones se hagan en otro hilo de procesamiento diferente del hilo principal

Android dispone de la clase AsyncTask pensada para dar solución a numerosas situaciones como las planteadas. La clase AsyncTask tiene un funcionamiento muy similar al que ofrece la clase SwingWorker cuando se trabaja en entornos Java-Swing

Para utilizarla tendremos que derivar una clase que descienda de AsyncTask y sobreescribir, al menos, el método doInBackground(). Todo lo que ejecutemos dentro de ese método se ejecutará en un hilo propio creado por AsyncTask, diferente del hilo que instanció nuestra clase, que generalmente será el hilo principal. AsyncTask ofrece otros métodos que nos permitirán comunicarnos con el hilo principal y transmitirle el progreso del proceso o el resultado final del mismo. Se trata de los métodos onProgressUpdate() y onPostExecute(). Si sobreescribimos estos métodos, todo lo que ejecutemos en ellos se ejecutará en el hilo principal. El método onProgressUpdate() se ejecutará cada vez que llamemos al método publishProgress() explicitamente desde dentro del método doInBackground(). El método onPostExecute() es llamado automáticamente cuando finaliza el proceso de doInBackground().

Hay tres clases genéricas que debemos indicar al derivar nuestra clase de AsyncTask: La primera corresponde al tipo de los parametros que recibe el método doInBackground(). La segunda corresponde a los parámetros que recibe el método onProgressUpdate(). La tercera clase es la de los valores devueltos por el método doInBackground() que coincide con los parámetros que se pasan al método onPostExecute()..

Veamos un ejemplo de un worker que actúa de contador. Dentro del método doInBackground() se va incrementando un contador cada segundo. Cada vez que incrementa el contador, manda publicar el resultado en un TextView existente en el UI.

Hay que destacar que el método ‘onProgressUpdate() recibe un array de parámetros del tipo declarado. Puede suceder que enviemos un mensaje a publicar y no sea publicado inmediatamente. En cualquier caso, cuando se ejecute el método, dara salida a los mensajes pendientes.

Vemos la forma de invocar la tarea desde la actividad principal: primero se instancia un objeto de la clase y luego se llama a su método ‘execute()‘. Podemos forzar que nuestra Activity espere el resultado de la tarea asíncrona llamando al método get() de la siguiente manera:

Esto forzará al hilo principal a esperar un resultado antes de seguir.

Por último, indicar que esta clase está pensada para tareas de corta duración, pongamos unos pocos segundos. Para tareas que lleven más tiempo, Android dispone de otros mecanismos específicos como los Service. También es posible utilizar el mecanismo de Threads y ThreadPools de Java. Encontraréis más documentación en Processes and Threads



Santiago Higuera (28 de febrero de 2014)

Feb 042014
 
Navegación a por estima

Vamos explicar el método que se utiliza en náutica para calcular la distancia y el rumbo directo entre dos puntos de los que se conocen sus coordenadas geográficas. El procedimiento proporciona valores precisos para puntos cuya separación sea menor de unas doscientas millas, esto es, unos 370 kilómetros. Es el denominado problema de estima inversa en navegación loxodrómica.

No vamos a entrar en las matemáticas del asunto, sino que vamos a utilizar una regla nemotécnica para memorizar dos triángulos rectángulos que nos permitirán deducir las fórmulas correspondientes. La justificación matemática se puede encontrar en cualquier tratado de náutica. A mí me gusta más que otros el libro de Luis Mederos ‘Navegación Astronómica’.

El problema que queremos resolver es, conocidas las coordenadas geográficas de dos puntos, calcular la distancia entre ellos y el rumbo directo a seguir para ir del primer al segundo punto. Llamaremos longitud1, latitud1, longitud2 y latitud2 a las coordenadas conocidas de los puntos.

Los dos triángulos rectángulos que nos proporcionan las fórmulas para relacionar las variables son los de la figura, donde el significado de las variables es el siguiente:

  • Δl: Incremento de latitud, esto es, latitud2-latitud1
  • R: rumbo para ir del primer punto al segundo. El rumbo es un ángulo medido desde el Norte hacia el Este (a derechas)
  • D: distancia entre los dos puntos
  • Ap: Apartamiento, esto es, la longitud del arco comprendido entre dos meridianos, medido a una latitud concreta. (ver ref)
  • lm: Latitud media, o sea, (latitud1+latitud2)/2
  • ΔL: incremento de longitud, es decir, (longitud2 – longitud1)

Calcularemos en primer lugar el Apartamiento, Ap, a partir de la fórmula que deducimos del segundo triángulo:

Ap =  ΔL * cos(lm)

En esta fórmula, como en las demás, es muy importante la utilización de las unidades correctas. En general, las coordenadas de los puntos inicial y final vendrán expresadas en grados sexagesimales. Tenemos que pasar todos los ángulos a radianes antes de comenzar a operar.

Una vez calculado el Apartamiento utilizaremos el primer triángulo de la figura para calcular el Rumbo y la Distancia:

R = atan( Ap /  Δl)
D = asin( Ap / sin(R) )

El rumbo obtenido estará expresado en radianes, por lo que habrá que pasarlo a grados para que sea de utilidad en el compás

La distancia también está en radianes. Habra que pasarla a minutos de arco, o lo que es lo mismo, a millas. Una vez en millas podremos expresarla en metros multiplicando por 1851:

D (millas) = D (minutos) = D (rad) * 180.0 /  π * 60 
D (metros) = D (millas) * 1851 

Os dejo un enlace a las rutinas java que resuelven el problema:

loxodromia.java

Por último indicar que la regla nemotécnica para memorizar los triángulos es la frase que aparece bajo la figura: Alrededor del Apartamento apareció la madre de Luis



Santiago Higuera (4 de febrero de 2014)