martes, 7 de enero de 2025

Teclado Midi con Arduino Leonardo.

Hace un tiempo estuve queriendo completar un proyecto de hacer un teclado midi a partir de un teclado chino barato. Para ello era consciente que necesitaba las siguientes implementos: 

  •  Un teclado viejo que no se use, lo único que se rescata son los pulsadores como entradas del Arduino 
  • Un Arduino Uno o como finalmente descubrí que era mejor el Arduino Leonardo pues tiene interface midi, directo, esto ayuda que no tengas que hacer un programa adicional para mapear las salidas de Arduino Uno y convertirlas en midi, en concreto con Arduino Leonardo puedo usar el teclado midi hasta en mi celular (con Garage Band u otro) 
  • Diodos para evitar el ghosting, esto ultimo me dio dolores de cabeza pues en algunas combinaciones de teclas simplemente no sonaban juntas, ellos porque al conectarse a un mismo punto la corriente se devolvía por donde no debía.
La parte difícil creo yo es descubrir la matriz de contactos que tiene cada teclado, en mi caso tenia dos bandas de cables de 8, los cuales modele como filas y columnas en Arduino. Para eso tienen que medir con el multimetro que terminales de los dos bandas de cables se cortocircuitaban al presionar las teclas del Piano.

Ejemplo: Para la primera nota Do se unían el primer pin del cable 1 con el primer pin del cable 2. Do# con el primer pin del cable 1 y el segundo del cable 2 y asi hasta llegar al ultimo pin del cable 2.

Como este teclado solo tiene 54 teclas, pude constatar que en la fila 7 solo usaba el pin 1 de ambos cables los demás no los usaba, y en la fila 8 solo se usaban del 4 al 8 pin.

Dado que cada elemento de la matriz es una nota esto tiene un equivalente en números para sonidos midi, considerando estos se hizo la siguiente matriz de sonidos que corresponde a mi teclado.

// Matriz de teclas con los valores MIDI asignados.
int keys[ROWS][COLS] = {
    {36, 37, 38, 39, 40, 41, 42, 43}, // Fila 1: Teclas 1-8
    {44, 45, 46, 47, 48, 49, 50, 51}, // Fila 2: Teclas 9-16
    {52, 53, 54, 55, 56, 57, 58, 59}, // Fila 3: Teclas 17-24
    {60, 61, 62, 63, 64, 65, 66, 67}, // Fila 4: Teclas 25-32
    {68, 69, 70, 71, 72, 73, 74, 75}, // Fila 5: Teclas 33-40
    {76, 77, 78, 79, 80, 81, 82, 83}, // Fila 6: Teclas 41-48
    {84, -1, -1, -1, -1, -1, -1, -1}, // Fila 7: Tecla 49 (especial)
    {-1, -1, -1, 85, 86, 87, 88, 89}  // Fila 8: Teclas 50-54 (especial)
};

Una vez definida la matriz se tienen que conectar a las salidas y entradas de Arduino es decir el pulsar una tecla unen los pines de Arduino Leonardo, para dicho fin use las siguientes salidas.

// Pines asignados a filas (A0-A5 y D2-D3).
byte rowPins[ROWS] = {A0, A1, A2, A3, A4, A5, 2, 3};

// Pines asignados a columnas (D4-D11).
byte colPins[COLS] = {4, 5, 6, 7, 8, 9, 10, 11};

Entonces, para conseguir Do se unen los pines A0 y D4, Do# A0 y D5 y así sucesivamente, el problema de ghosting que tenia era al comento de conectar A0 D4 y A1 D4 por lo que probablemente la corriente se devolvía por el punto D4 lo cual hacia que no sonaran las dos notas juntas, por lo que es en ese lugar donde deberían ir los diodos (8 en total). Finalmente asi me quedo el conexionado. En la parte de las bandas que están con cinta negra, se incluyó los 8 diodos en cada una para evitar el ghosting.







Aproveché para poner un cambio de escalas en los pies D12 y D13 con GND de esta manera puedo cambiar de escala arriba o abajo y al presionar los dos vuelvo a la escala original, eso se logra con un offset a los valores que incluí arriba en la matriz.

Este es el código final tal como resultado para este proyecto.



#include <MIDIUSB.h>

//con Arduino Leonardo: código by Edward Angelino

const byte ROWS = 8; // Número de filas.
const byte COLS = 8; // Número de columnas.

// Matriz de teclas con los valores MIDI asignados.
int keys[ROWS][COLS] = {
    {36, 37, 38, 39, 40, 41, 42, 43}, // Fila 1: Teclas 1-8
    {44, 45, 46, 47, 48, 49, 50, 51}, // Fila 2: Teclas 9-16
    {52, 53, 54, 55, 56, 57, 58, 59}, // Fila 3: Teclas 17-24
    {60, 61, 62, 63, 64, 65, 66, 67}, // Fila 4: Teclas 25-32
    {68, 69, 70, 71, 72, 73, 74, 75}, // Fila 5: Teclas 33-40
    {76, 77, 78, 79, 80, 81, 82, 83}, // Fila 6: Teclas 41-48
    {84, -1, -1, -1, -1, -1, -1, -1}, // Fila 7: Tecla 49 (especial)
    {-1, -1, -1, 85, 86, 87, 88, 89}  // Fila 8: Teclas 50-54 (especial)
};

// Pines asignados a filas (A0-A5 y D2-D3).
byte rowPins[ROWS] = {A0, A1, A2, A3, A4, A5, 2, 3};

// Pines asignados a columnas (D4-D11).
byte colPins[COLS] = {4, 5, 6, 7, 8, 9, 10, 11};

// Pines para los botones de cambio de octava
const byte btnUp = 12;    // Botón para subir la octava
const byte btnDown = 13;  // Botón para bajar la octava

// Variables para manejar la octava
int octaveShift = 0;      // Cambio de octava actual

// Variables para almacenar el estado de las teclas.
bool keyState[ROWS][COLS] = {false};

void setup() {
    Serial.begin(115200); // Depuración serial.

    // Configurar los pines de las filas como salidas y las columnas como entradas.
    for (int i = 0; i < ROWS; i++) {
        pinMode(rowPins[i], OUTPUT);
        digitalWrite(rowPins[i], HIGH); // Inicializar en estado alto (no activado).
    }

    for (int i = 0; i < COLS; i++) {
        pinMode(colPins[i], INPUT_PULLUP); // Las columnas como entradas con resistencia pull-up.
    }
    
    // Configurar los botones
    pinMode(btnUp, INPUT_PULLUP);
    pinMode(btnDown, INPUT_PULLUP);
}

void loop() {
    // Manejar botones de octava
    handleOctaveButtons();

    // Recorremos todas las filas.
    for (int row = 0; row < ROWS; row++) {
        // Configuramos la fila actual como baja (activada) y las demás como alta.
        for (int i = 0; i < ROWS; i++) {
            digitalWrite(rowPins[i], (i == row) ? LOW : HIGH);
        }

        // Revisamos las columnas para ver si alguna tecla está presionada.
        for (int col = 0; col < COLS; col++) {
            if (digitalRead(colPins[col]) == LOW) { // Si la columna está baja, la tecla está presionada.
                if (!keyState[row][col]) { // Si el estado anterior no era presionado.
                    keyState[row][col] = true; // Actualizamos el estado a presionado.
                    int tecla_presionada = keys[row][col];

                    if (tecla_presionada != -1) {
                        // Ajustar la nota según la octava
                        int adjustedNote = tecla_presionada + octaveShift;
                        if (adjustedNote >= 0 && adjustedNote <= 127) {
                            // Enviar mensaje MIDI de Note On
                            noteOn(0, adjustedNote, 127); // Canal 0, nota, velocidad
                            MidiUSB.flush();
                        }
                    }
                }
            } else {
                if (keyState[row][col]) { // Si la tecla fue liberada.
                    keyState[row][col] = false; // Actualizamos el estado a liberado.
                    int tecla_presionada = keys[row][col];

                    if (tecla_presionada != -1) {
                        // Ajustar la nota según la octava
                        int adjustedNote = tecla_presionada + octaveShift;
                        if (adjustedNote >= 0 && adjustedNote <= 127) {
                            // Enviar mensaje MIDI de Note Off
                            noteOff(0, adjustedNote, 0); // Canal 0, nota, velocidad 0
                            MidiUSB.flush();
                        }
                    }
                }
            }
        }
    }
    delay(10); // Retardo para evitar rebote (debouncing)
}

// Función para manejar los botones de cambio de octava
void handleOctaveButtons() {
    bool upPressed = digitalRead(btnUp) == LOW;
    bool downPressed = digitalRead(btnDown) == LOW;

    if (upPressed && downPressed) {
        // Si ambos botones están presionados, resetea la octava
        octaveShift = 0;
        Serial.println("Resetear octava");
        delay(200); // Anti-rebote
    } else if (upPressed) {
        // Subir una octava
        octaveShift += 12;
        Serial.println("Subir octava");
        delay(200); // Anti-rebote
    } else if (downPressed) {
        // Bajar una octava
        octaveShift -= 12;
        Serial.println("Bajar octava");
        delay(200); // Anti-rebote
    }
}

// Función para enviar mensajes MIDI Note On
void noteOn(byte channel, byte pitch, byte velocity) {
    midiEventPacket_t event = {0x09, static_cast<uint8_t>(0x90 | channel), pitch, velocity};
    MidiUSB.sendMIDI(event);
}

// Función para enviar mensajes MIDI Note Off
void noteOff(byte channel, byte pitch, byte velocity) {
    midiEventPacket_t event = {0x08, static_cast<uint8_t>(0x80 | channel), pitch, velocity};
    MidiUSB.sendMIDI(event);
}


El nuevo teclado ahora solo se conecta por usb a la PC (o celular)  y ya se tiene un teclado midi, en un monitor midi en mi caso uso "midi monitor" se puede ver que nota(s) se esta(n) pulsando.





martes, 6 de abril de 2021

 Series Restringidas : 

Uno de lo principales problemas que se enfrenta al momento de hacer pronósticos de series temporales con la metodología ARIMA univariante, es la posibilidad de que el valor escocástico medio no necesariamente represente los valores anuales que se esperan,  aunque el intervalo de confianza si lo considere ver gráfico.




En el gráfico, el pronóstico original (negro) es la estimación inicial con el modelo Arima, la linea roja recortada representa la serie que se espera y que suma los valores meta anuales.

El intervalo de confianza (color gris) nos brinda un espacio estadísticamente valido para poder obtener estimaciones mensuales que satisfacen condiciones anuales, cómo es el caso del gráfico en el cual se impuso valores promedios anuales (7.5% todos los siguientes años).

Serie Original    %Serie Restringida     % Impuesto
202038567.7938567.79
202142083.98   9.1%41460.387.5%
202243839.394.2%44569.917.5%
202345506.843.8%47912.667.5%

El indicador que mide si las tasas son compatibles y por ende que se esta restringiendo adecuadamente la serie original considerando el intervalo  de confianza  (95%) es el Kcal, el cual se compara con los grados de libertad (χ2), este estadístico verifica si la restricción esta dentro de la region de compatibilidad o intervalo de confianza.

Kcal < χ2

7.347649235181968 < 7.8147


Dado que en el cálculo actual estamos considerando casi los limites el valor es cercano al máximo, este valor es 0.0 cuando se estima con las mismas tasas.


Con esta metodología se puede unir horizontes temporales de modelos Arima, en el caso mostrado se une estimaciones econométricas anuales ( variables explicativas son el PBI, Precio de energía y Población), con el modelo Arima univariante (puramente data histórica de la serie).


El modelo de restricciones utiliza como datos.


Datos del modelo Arima:

Ecuaciones de diferencias regular y estacional de la serie (Función L) = (1-L)*(1-L^12)

Ecuaciones de media móvil (Función de Theta y L) = (1-0.159399*L-0.197145*L^2)*(1-0.925187*L^12)

Ecuación de autoregresivo (Función de Phi y L) = 1

Desviación estándar (SD) del modelo = 0.011548

 

     ⎛              12⎞ ⎛            2                 ⎞ 

     ⎝1 - 0.925187⋅L  ⎠⋅⎝- 0.197145⋅L  - 0.159399⋅L + 1⎠

Yt = ───────────────────────────────────────────────────  * Et

                         ⎛     12⎞                 

                 (1 - L)⋅⎝1 - L  ⎠    


Pronóstico a restringir.

Pronóstico historia y

Pronostico sin atípicos. (si se tiene modelado atípicos ver figura)


 

El resultado se muestra en la primera figura.

Implementación del Modelo de Restricciones:

Los cálculos necesarios se realizan con rutinas Python en Julia para resolver ecuaciones de manera algebraica, considerando la metodología explicada en las referencias [1][2][3]

se tiene que instalar el programa ArimaRest (by Edward Angelino), en Julia.

(v1.0) pkg> add https://github.com/EdwardAngelino/ArimaRest.jl

julia> using ArimaRest

julia> datos=leedatostxt("DatosArimaRest.txt")

julia> restringido(datos,"salida.csv")


Asimismo se tiene una interface gráfica (SeriesRest) hecha en QT (C++) que llama al programa ArimaRest internamente.

cuyo instalador se tiene en el siguiente link, previamente deberas tener instalado Julia y ArimaRest. 

instalador


[1] Pronósticos con restricciones para series de tiempo, Jesús Alberto Albarracín, Harney Palacios Bejarano, 2011. Link

[2] Pronósticos restringidos con modelos de serie de tiempo multiples y su aplicación para evaluar metas de política económica en Mexico, Victor M, Guerrero 2005. Link

[3] Pronósticos con restricciones en series de tiempo univariadas: Aplicación al seguimiento del PIB de Mexico en 2001, Victor M. Guerrero 2001. Link

martes, 24 de octubre de 2017

Como hacer una CNC con restos de DVDs usando ARDUINO


Hardware
------------
1. Arduino UNO
2. CNC Shield
3. Drivers de motores DRV8825
4. Fuente de Poder 12V
5. Cables
6. Protoboard
7. Motores de paso de 2 DVDs
8. Servo Turnigy pequeño
9. Bastidor con dos MDF y algo para el lapicero.


Programas
-------------

1. Arduino IDE
2. GRBL-servo master
3. Universal G-Code Sender.
4. Inkscape
5. Extension Inkscape para servo.


El controlador Arduino, CNC Shield y el driver de motores se conectan, luego se tiene que ajustar la tensión de operación de esos motores, maso menos la corriente que circula Corriente limite = VREF * 2 (Amp), yo le puse 200 mV se obtiene 400 mA, lo cual es mas que suficiente para los motores de paso de DVD. para ello se tiene que energizar CNC Shield y con un perillero modificar la tensión, usando como punto positivo el potenciometro, también se puede conectar con cocodrilo en el perillero.
Luego instalamos Motores de paso, son 4 cables 2 bobinados si tuviera que invertir el sentido se cambia entre pares de bobinados, para determinar cada par se conecta un diodo y mueve el motor con la mano debería encender cada para de bobinas.

Finalmente conectamos el servo 5V (rojo), GND (Negro) y el de señal (amarillo o color mas claro) en Z+ ya que esté esta conectado directamente a D11 en Arduino ver gráfico.

Conectamos Arduino a la PC  con el cable USB , y CNC Shield con 12 V, yo tenia la fuente con la que cargo baterías LiPo, asi que lo puedo utilizar.  Se tiene que esperar que el driver configure en windows o Mac Arduino, luego al arrancar Arduino IDE, se tiene que direccionar el puerto (COM*) y le nombre de Arduino, se comprueba leyendo datos de la placa desde el mismo menu de herramientas.

Una vez comprobado que todo funciona adecuadamente, subimos GRBL, el cual es un firmware que permite a Arduino operar CNC Shield y motores de paso  copiamos en /Documentos/Arduino/ Librerias  la carpeta GRBL -Servo. Luego lo levantamos desde Arduino en la opcion Archivo/ejemplos.

Abrimos Universal G-Code Sender, escojamos puerto COM que obtuvimos en Arduino IDE y baud: 115200, si todo esta bien no hay mas mensajes y podemos entrar comandos desde linea comandos del programa.
$$ : para ver configuración.
 M5 : para poner el servo en posición central.
M3S030: Para mover el servo 30 grados.
mas información en este link,  En nuestro caso  se tuvo que invertir el sentido de giro del servo.

luego pruebas paso a paso con X+, X-, Y+, Y- (botones) esta configuración permite como máximo un dibujo de 33 x 33 mm. para efectos del inkscape.

hacer dibujos en Inkscape: El concepto es vectorizar los dibujos  que se quiera obtener, primero se importa el dibujo luego se convierte a vectorizado, finalmente ejecutar la extensión Electronica Hoje, se incluyendo los siguientes parámetros:
 M3, M5, 2000, 2000, 30, 0.5
Directorio y el nombre de archivo

si todo OK, ya tenemos el archivo gcode correcto, este lo abrimos en Gcode Sender y visualizamos, verficamos que no exceda los limites de impresion y  enviamos.

En este momento hice pruebas con lapiz o lapicero, con resultado regular, me falta probar con lapicero de tinta, con el que espero tener mejores resultado. Mucho tiene que ver el bastidor y la prescicion.






lunes, 25 de abril de 2016

Hoy - Gian Marco

Tengo marcado en el pecho,
todos los días que el tiempo
no me dejó estar aquí.

Tengo una fe que madura,
que va conmigo y me cura
desde que te conocí.

Tengo una huella perdida
entre tu sombra y la mía,
que no me deja mentir.

Soy una moneda en la fuente;
tú mi deseo pendiente
mis ganas de revivir.

Tengo una mañana constante
y una acuarela esperando
verte pintado de azul.

Tengo tu amor y tu suerte
y un caminito empinado.
Tengo el mar del otro lado:
tú eres mi norte y mi sur

(Coro)
Hoy voy a verte de nuevo
voy a envolverme en tu ropa,
susúrrame en tu silencio
cuando me veas llegar.
Hoy voy a verte de nuevo,
voy a alegrar tu tristeza:
vamos a hacer una fiesta
pa' que este amor crezca más.

Tengo una frase colgada,
entre mi boca y mi almohada,
que me desnuda ante ti.

Tengo una plaza y un pueblo
que me acompañan de noche
cuando no estas junto a mí.

Tengo una mañana constante
y una acuarela esperando
verte pintado de azul.

Tengo tu amor y tu suerte
y un caminito empinado.
Tengo el mar del otro lado:
tú eres mi norte y mi sur.

(Repetir coro)



viernes, 6 de junio de 2014

Instalar CAS en TI-Nspire CX non CAS

La semana pasada me compre una calculadora TI-Nspire CX; estaba todo bien hasta que quería hacer cálculos algebraicos :(.... la verdad nunca tuve antes una Texas Instrument, luego con pena y un poco contrariado vi que esta versión no lo hace, sino hay que comprar la otra CX CAS. (creo que son 30 US$ mas).

Indagando en paginas como las indicadas lineas abajo, entendí que el hardware de ambas calculadoras son iguales solo que una tiene restricciones (software control) para no poder bajar de versión o actualizar a la CAS, y es solo por una estrategia comercial, para suerte mía.

En fin, para liberar esta máquina se tiene que hacer varias cosas entre ellas copiar archivos en el área de Documentos.

1. Se debe instalar TI-Nspire Computer Link para conectar a la PC
2. Asimismo el software para utilizar y navegar por carpetas de la TI TI-Nspire Student Software

3. Para hacer la transformacion se requiere correr nless 3.1, intercambia entre SO inclusive permite tener dos SO.

4. Asimismo nsNandMgr el cual permite eliminar las restricciones al inicio del arranque o instalacion de OS.

5. TNOC para disminuir el tamaño de los SO y quitarle algunas cosas como Boot2.

6. TI-NspireCXCAS-3.1.0.392.tcc el SO que incluye CAS (ojo que este tiene extensión tcc)

En una carpeta aparte se debe tener las carpetas ndless, nlaunch, nsNandMgr y el archivo nlaunch.tco todas estas de los archivos antes mencionados.








7. el archivo TI-NspireCXCAS-3.1.0.392.tcc se debe modificar, primero con el software TNOC quitando Boot2, luego se renombra a phoenic.tns. he incluirlo en la carpeta nlaunch, reemplazar por el anterior. Luego mediante la conexión copiar las primeras 3 carpetas. Se debe ejecutar nsNandMgr para activarla.

8. Reiniciar la TI-Nspire y borrar el SO actual esto con presionando primero "reset" luego las teclas EE+Enter+doc luego sin soltar estas tres ultimas soltar "reset". Entonces se escoge opción 2 y luego 1.

 9. La TI-Nspire ya no tiene SO y esta a la espera del mismo con la barra de avance atorada en el medio, utilizando la conexion se instala el archivo "nlaunch.tco" instalar SO asi:
si sale error no hay problema, entonces comienza a utilizar el SO que esta en phoenic.tns (que tiene CAS). Finaliza y no hay mas errores pasa a nsNandMgr y hay que escojer el SO que tiene CAS en mi caso era BootD, luego se sale con 0.

Luego de ello todo Ok y ya se tiene todas las funciones de CAS en la TI-Nspire CX .






Referencias:

https://github.com/bibanon/android-development-codex/wiki/Nspire-Nlaunch
http://www.omnimaga.org/general-calculator-help/installing-cas-on-ti-nspire-cx-non-cas/60/
https://www.youtube.com/watch?v=brd8q0huPuY

lunes, 28 de enero de 2013

Uso de TortoiseSVN / Subversion

Bueno después de un tiempo, vuelvo para comentarles sobre el uso de este interesante controlador de versiones totalmente para windows.

 Bueno no diré de donde bajarlo pues de seguro ya lo saben (googlea)

La mecánica de SVN en este caso de tortoise es:
  1. Se crea un repositorio. 
  2. Se sacan copia del repositorio (copias de trabajo) 
  3. Desde las copias de trabajo, se actualiza el repositorio, como una nueva versión. 
  4. Se puede sacar desde el repositorio copia de cualquier versión que se tiene almacenada. 
  5. Se puede trabajar en paralelo con una misma versión y luego combinar y resolver conflictos de ambos archivos. 
Detallo cada uno de los pasos con comandos de windows:

  1. Una vez instalado el Tortoise se añadirá al menú contextual las opciones interesantes del mismo. La forma mas segura de crear un repositorio es importarlo, para tal fin se sigue los siguientes pasos: - Se crea una carpeta donde se guardara el repositorio: ejemplo
    "svnEjemplo", Bueno alli se hace menú contextual /TortoiseSVN/Create Repository here.. luego "create folder structure".
    Luego hay que importar los archivos que formaran parte del repositorio. Para ello uno se ubica en la carpeta donde están los archivos y con el menú contextual en /TortoiseSVN/Import...
    Pide como datos la dirección del repositorio en mi caso como esta en el disco D puse
      file:///D:/ejemSVN, se pude añadir un comentario. (ojo son 3 /// para direccionar a disco).
    Una ves importado ya se puede prescindir de los archivos de la carpeta importada, pues todo estara ahora en D:/ejemSVN, inclusive las futuras modificaciones o versiones.
  2. Para poder utilizar los archivos del repositorio uno tiene que realizar un checkout del repositorio para ello se debe crear una nueva carpeta donde se tendrán los archivos de trabajo. ejemplo "proy1", igual con menu contextual SVN Checkout sobre la carpeta creada. en la ventana también se pude indicar la versión que queremos en "Revision", de otro modo sera la ultima o mas reciente. Si todo esta bien obtendremos en "Proy1" todos los archivos y según la versión requerida.
  3. Una ves trabajados los archivos con actualizaciones o nuevos archivos (este ultimo se usa el add)  se realiza una actualización de version con el  menu contextual SVN commit. Ahora cuando hagamos otra copia de trabajo se tendra siempre la ultima version. E inclusive si ya teniamos una copia de trabajo la podemos actualizar con el comendo SVN Update..
  4. El procedimiento descrito anteriormente, es para trabajo secuencial, es decir SVN checkout - Modificaciones - SVN Commit.. / SVNUpdate. Pero que sucede si dos personas hacen cada uno una copia de trabajo y actualizan en paralelo los dos archivos. Ello para el Tortoise indicara un error, pues las versiones no estaran actualizadas. Para resolver ese problema se tiene que combinar (merge) y corregir (resolved), sea de manera manual o automática con Tortoise.
    En mi caso tuve el problema de trabajar un archivo word de manera paralela, para suerte nuestra se tiene que el Word puede hacer el combinado con lo cual nos ahorra bastante trabajo. El procedimiento es el siguiente:
    • Modifican 1
      Modifican 2
    • Commit en 1 - Aceptado los cambios de 1
    • Update en 2 : Muestra error por q el archivo esta desactualizado. Sirve para que se muestre el error
    • Commit en 2 (color rojo archivo) : muestra donde esta los errores para hacer commit; resolver conflictos con doble click en archivo rojo, se abre word con opcion - merge : finalmente solo se tiene que guardar.
    • Resolved... en 2:  Nueva version con los cambios de 1 y 2.
    • Commit en 2 : version final recoge lo de 1 y 2
    • Update 1: para tener la ultima version para 1 tambien











martes, 14 de junio de 2011

MPICH2 - Qdevelop/QT C++

Esta librería útil para procesamiento en paralelo,
se detalla el procedimiento para echarlo andar, ahora con resultados interesantes:

1. Instalar el programa correcto de mpich , la versión propuesta o actual todavía tiene problemas con la librería mpi.lib.

2.Copiar los archivos lib a la carpeta del \\Qt\4.3.2\lib

3. Lamarlos desde el compilador en mi caso uso QDevelop y tengo adicionar a la variable

INCLUDEPATH += "c:\Program Files\MPICH2\include"
LIBS += -lmpi

4. Compilar linkear y todo bien para el caso del QT se tiene estos:

g++ -c -O2 -frtti -fexceptions -mthreads -Wall -DUNICODE -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_NO_DEBUG -DQT_GUI_LIB -DQT_CORE_LIB -DQT_THREAD_SUPPORT -I"e:\Qt\4.3.2\include\QtCore" -I"e:\Qt\4.3.2\include\QtCore" -I"e:\Qt\4.3.2\include\QtGui" -I"e:\Qt\4.3.2\include\QtGui" -I"e:\Qt\4.3.2\include" -I"..\..\..\Program Files\MPICH2\include" -I"e:\Qt\4.3.2\include\ActiveQt" -I"release" -I"." -I"e:\Qt\4.3.2\mkspecs\default" -o release\pi.o src\pi.cpp

g++ -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -Wl,-s -Wl,-subsystem,console -mthreads -Wl -o "release\Multi.exe" release\pi.o -L"e:\Qt\4.3.2\lib" -lmpi -lQtGui4 -lQtCore4

Luego de muchos intentos y problemas como que el printf( del C++ no funciona correctamente y además detiene la ejecución del MPI (???) conseguí el código correcto para correr el calculo de pi.
Codigo de Calculo de Pi#include 
#include
#include
#include "math.h"

int main(int argc, char *argv[])
{
int n, myid, numprocs, i;
double PI25DT = 3.141592653589793238462643;
double mypi, pi, h, sum, x;
double starttime, endtime;

MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
while (1) {
if (myid == 0) {
fprintf(stdout,"Enter the number of intervals: (0 quits) ");
fflush(stdout);
//qDebug() << "Enter the number of intervals: (0 quits) ";
scanf("%d",&n);
}
starttime = MPI_Wtime();

MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);
if (n == 0)
break;
else {
h = 1.0 / (double) n;
sum = 0.0;
for (i = myid + 1; i <= n; i += numprocs) {
x = h * ((double) i - 0.5);
sum += (4.0 / (1.0 + x*x));
}
mypi = h * sum;
MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0,
MPI_COMM_WORLD);
endtime = MPI_Wtime();
if (myid == 0){
fprintf(stdout,"Pi es = %lf y el error es %lf ", pi, fabs(pi - PI25DT));
fprintf(stdout,"el tiempo fue %f seg\n",endtime-starttime);
fflush(stdout);}


}
}
MPI_Finalize( );
return 0;
}


Asimismo para poder ejecutarlo en paralelo en las diferentes opciones se usa el mpiexec asi:
Usos del mpiexec
#Correr en la misma máquina 10 procesos
mpiexec -n 10 pi.exe

#Correr en otro maquina n procesos
mpiexec -n 10 -host nombrehost pi.exe

#Correr en varias maquinas n procesos
#se indica el archivo hosts.txt con los nombre de las maquinas en el formato
#nombredelhost1:5
#nombredelhost1:4
#la suma de nro de procesos distribuidos deberia ser n
mpiexec -machinefile hosts.txt -n 9 pi.exe

#Esta sirve para correr 5 casos de *.exe. en las maquinas listadas en hosts.txt
#se tuvo que añadir el entorno pues el cplex requiere ubicacion del archivo de entorno.
mpiexec -env CPLEXLICDIR E:\ -machinefile hosts.txt -n 5 calc2.exe

Se Alumbra la vida!!



Quisiera que la vida aprenda
El verso que voy a contar
Se trata de un sueño posible
Conciencia de amor a recuperar
Soñé que paseábamos libres
Sentí que, no estábamos solos
Que todo el mundo se puso a cantar
Y que así muy juntos, armaban acordes de paz

Que nuestra canción
Se elevaba hacia el futuro
Que el cielo era nuestra profunda verdad
Soné que los vientos eternos y activos
Soplaban al centro de la humanidad
Y así la inocente sonrisa de un niño
se vuelve cada día un Sol matinal
que su hambre se acaba, por fin en este siglo
se alumbra la vida, la vida , vida

Se alumbra la vida, se alumbra la vida
Quisiera que la vida aprenda el verso que voy a cantar
Se alumbra la vida, se alumbra la vida
Se trata de un sueño posible, conciencia de amor a recuperar
Se alumbra la vida, se alumbra la vida
Que se alumbre la vida, se alumbre la vida, vida
Se alumbra la vida, se alumbra la vida

viernes, 1 de octubre de 2010

Firmar y cifrar correo o informacion con GnuPG

Idea general:

Se utiliza firma digital para certificar que el que envia el correo es la persona indicada, no hay suplantacion. asimismo para cifrar archivos (correos)
la firma digital esta publicada en internet y es generada como texto cifrado (complejo), en internet por si se requiere descargar o se olvido uno.
El cliente de correo debera verificar y comparar con la firma publica (privada en caso de cifrado) almacenada en la maquina para mostrar los correos enviados por otra persona.
asimismo el programa desencriptador (seahorse kgpg etc, gpg4win) revisara las firmas (publicas o privadas) instaladas en la maquina para poder abrir los archivos cifrados.



Linux tiene gpg por default
el programa interesante es seahorse (equivalente menor a gpg4win)
te permite general las firmas digitales para cualquier usuario.

Este se puede configurar en el correo para cifrar o firmar se necesida el ID.

pasos:
1. Crear una firma con seahorse, pide clave todo por default. igual para gpg4win
2. Escojer esa opcion en seguridad de correo en evolution (sera igual en el outlock?)
3. en linux o en windows ya se puede cifrar y firmar cosas ejemplo un archivo excel o una carpeta el resultado tendra extension
*.asc o gpg (el primero en correos (pgp) y el segundo para cosas desde el desktop) Click derecho en ambos.
4. la firma publica se debe compartir para poder activar el envio de correos a los destinatarios.
5. para tener una clave privada o todas las claves se debe exportar desde seahorse (igual se puede importar de alli o desde gpg4win)
6. Se requiere clave privada para poder cifrar correos y archivos para un usuario cualquiera.(???)


Nota: En gpg4win no se debe instalar Kleopatra para poder tener facilidad en click derecho.
hay que exportar la clave privada con comando DOS en windows ver internet


Pendientes:
. investigar como configurar en Outlock el envio y recepcion de correos cifrados y seguros
. como firmar sitios para permitir descargar ejemplo: debian

miércoles, 28 de abril de 2010

Compilar aplicaciones con glpk en Debian y windows7

Para los desarrolladores y usuarios que estan interesados en Programacion lineal, y software libre se tiene la opcion glpk para linux y glpk para windows

La idea del presente blog es explicar como consegui compilar mi primera aplicacion incluyendo la libreria glpk tanto en debian lenny como en Windows7.

para lenny la instalacion de glpk es sencilla, basta con usar Synaptic e instalar todo lo que tenga que ver con glpk (incluyendo libglpk-dev)

en windows se tiene que bajar el setup de gplk.

Ahora viene lo interesante con los programas compiladores y enlazadores, en mi caso usare Qdevelop tanto para windows como para linux.
antes de todo se debe instalar Qt4 en ambos sistemas, de eso nos ocuparemos en otro blog. Solo comentare que Qdevelop en linux se direcciona automaticamente al la instalacion de Qt realizada, mientras que en windows hay que compilar Qt y luego direccionar a las carpetas Qt creadas.

Si tenemos el mismo codigo siguiente para Lenny y Win

#include "stdio.h"
#include "stdlib.h"
#include "glpk.h"

int main(void)
{     glp_prob *lp;
int ia[1+1000], ja[1+1000];
double ar[1+1000], z, x1, x2, x3;
s1:   lp = glp_create_prob();
s2:   glp_set_prob_name(lp, "sample");
s3:   glp_set_obj_dir(lp, GLP_MAX);
s4:   glp_add_rows(lp, 3);
s5:   glp_set_row_name(lp, 1, "p");
s6:   glp_set_row_bnds(lp, 1, GLP_UP, 0.0, 100.0);
s7:   glp_set_row_name(lp, 2, "q");
s8:   glp_set_row_bnds(lp, 2, GLP_UP, 0.0, 600.0);
s9:   glp_set_row_name(lp, 3, "r");
s10: glp_set_row_bnds(lp, 3, GLP_UP, 0.0, 300.0);
s11: glp_add_cols(lp, 3);
s12: glp_set_col_name(lp, 1, "x1");
s13: glp_set_col_bnds(lp, 1, GLP_LO, 0.0, 0.0);
s14: glp_set_obj_coef(lp, 1, 10.0);
s15: glp_set_col_name(lp, 2, "x2");
s16: glp_set_col_bnds(lp, 2, GLP_LO, 0.0, 0.0);
s17: glp_set_obj_coef(lp, 2, 6.0);
s18: glp_set_col_name(lp, 3, "x3");
s19: glp_set_col_bnds(lp, 3, GLP_LO, 0.0, 0.0);
s20: glp_set_obj_coef(lp, 3, 4.0);
s21: ia[1] = 1, ja[1] = 1, ar[1] = 1.0; /* a[1,1]  = 1 */
s22: ia[2] = 1, ja[2] = 2, ar[2] = 1.0; /* a[1,2]  = 1 */
s23: ia[3] = 1, ja[3] = 3, ar[3] = 1.0; /* a[1,3]  = 1 */
s24: ia[4] = 2, ja[4] = 1, ar[4] = 10.0; /* a[2,1] = 10 */
s25: ia[5] = 3, ja[5] = 1, ar[5] = 2.0; /* a[3,1]  = 2 */
s26: ia[6] = 2, ja[6] = 2, ar[6] = 4.0; /* a[2,2]  = 4 */
s27: ia[7] = 3, ja[7] = 2, ar[7] = 2.0; /* a[3,2]  = 2 */
s28: ia[8] = 2, ja[8] = 3, ar[8] = 5.0; /* a[2,3]  = 5 */
s29: ia[9] = 3, ja[9] = 3, ar[9] = 6.0; /* a[3,3]  = 6 */
s30: glp_load_matrix(lp, 9, ia, ja, ar);
s31:  glp_simplex(lp, NULL);
s32:  z = glp_get_obj_val(lp);
s33:  x1 = glp_get_col_prim(lp, 1);
s34:  x2 = glp_get_col_prim(lp, 2);
s35:  x3 = glp_get_col_prim(lp, 3);
s36:  printf("\nz = %g; x1 = %g; x2 = %g; x3 = %g\n", z, x1, x2, x3);
s37:  glp_delete_prob(lp);
return 0;
}/* eof */


En ambos sistemas (Debian y Win) solo nos queda crear un nuevo proyecto vacio en Qdevelop, luego incluir un archivo sample.cpp con el codigo lineas arriba, glpk.h deberia ser llamado automaticamente, en ambos sistemas si esta instalado correctamente glpk, en todo caso se detalla la direccion donde esta instalado en propiedades del proyecto (en en otras palabras en el archivo Makefile en la seccion INCPATH = -I/C:\Program Files\GnuWin32\include).

luego para enlazar la libreria de manera implicita (esta manera pedira los *.dlls requeridos o los *.so en linux para correr la aplicacion, los cuales deben ir junto con el programa) en linux tuve que añadir en las propiedades del proyecto la libreria glpk entonces eso en Makefile.

LIBS          = $(SUBLIBS)  -L/usr/lib -lglpk -lm -lQtGui -lQtCore -lpthread



y en windows se tuvo que añadir la libreria se añade de igual manera solo que ahora se le dice glpk.lib, si no la encuentra poner la libreria en la direccion del codigo (Makefile-Release).

LIBS        =        -L"d:\Qt\4.3.2\lib" glpk.lib -lQtGui4 -lQtCore4


Luego de compilar y correr sin errrores se obtiene lo siguiente en la ventana de Depuracion o Salida.

Depurando...

(gdb) (gdb) (gdb) (gdb) (gdb) Starting program: /home/eangelino/glpkxxx/glpkxxx
[Thread debugging using libthread_db enabled]
* 0: objval = 0.000000000e+00 infeas = 0.000000000e+00 (0)
* 2: objval = 7.333333333e+02 infeas = 0.000000000e+00 (0)


OPTIMAL SOLUTION FOUND

z = 733.333; x1 = 33.3333; x2 = 66.6667; x3 = 0

Program exited normally.



Actualizacion: En windows luego de instalar el glkp solo ejecutables se debe incluir en el Qt, en propiedades del proyecto:

INCLUDEPATH : "C:\Program Files\GnuWin32\include"
LIB : "C:\Program Files\GnuWin32\lib\glpk.lib"

lunes, 1 de febrero de 2010

chm to pdf en debian lenny

Baje un libro en formato chm, el formato es bueno sin embargo para poder leerlo en pdf o en Mobipocket no funciona correctamente.
en fin enconre una solucion en Debian se llama chm2pdf

una herramienta de facil uso, en debian se tiene que instalar previamente.

apt-get install htmldoc libchm-bin python-chm


luego bajar el script chm2pdf-0.9.1.tar.gz

#Descomprimir
tar -xzvf chm2pdf-0.9.1.tar.gz

#Ubicarse en la carpeta.
$:cd chm2pdf-0.9.1.tar.gz

#Instalar
$:python setup.py install

#Finalmente se usa.
$chm2pdf --book ' o puedes usar --webpage


al inicio me daba problemas, yo creo por falta de las librerias, sin embargo lo que hice es crear
las carpetas /tmp/... que hacia referencia el script y funcionaba mediocremente.
al final con todas las librerias, si funciona correctamente.

Finalmente el script nos devuelve un pdf con la informacion.

martes, 12 de enero de 2010

jueves, 10 de septiembre de 2009

Configurar mi mouse G5 logitech en Debian Lenny

Primero que nada hay que revisar que si funciona el mouse al conectarlo al usb

$:lsusb
Bus 005 Device 005: ID 0ac8:c002 Z-Star Microelectronics Corp. Visual Communication Camera VGP-VCC1
Bus 005 Device 004: ID 148f:2573 Ralink Technology, Corp. RT2501USB Wireless Adapter
Bus 005 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 004: ID 062a:0000 Creative Labs Optical mouse
Bus 001 Device 005: ID 046d:c049 Logitech, Inc. G5 Laser Mouse
Bus 001 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub


ok si esta en el Bus001 Device 005

indagando un poco llegue a parar en la web de los linux gamers todo muy bueno mas aun el test que se le puede hacer al dispositivo sin haber instalado nada:


Test del mouse conectado solamente sin funcionar:
Primero ver que evento es el que usa el raton:

$: cat /proc/bus/input/device
..............
I: Bus=0003 Vendor=046d Product=c049 Version=0111
N: Name="Logitech USB Gaming Mouse"
P: Phys=usb-0000:00:1d.0-2/input0
S: Sysfs=/class/input/input2
U: Uniq=
H: Handlers=mouse1 event2
B: EV=17
B: KEY=ffff0000 0 0 0 0 0 0 0 0
B: REL=143
B: MSC=10
...............



luego hay que bajar "evdev-key-btn-test.c" googleando lo encuentras
despues compilarlo

$:gcc -o evdev-key-btn-test evdev-key-btn-test.c


despues probar...

$: evdev-key-btn-test /dev/input/event2
Supported Keys:
Key 0x110 (Left Button)
Key 0x111 (Right Button)
Key 0x112 (Middle Button)
Key 0x113 (Side Button)
Key 0x114 (Extra Button)
Key 0x115 (Forward Button)
Key 0x116 (Back Button)
Key 0x117 (Task Button)
Key 0x118 (Unknown key)
Key 0x119 (Unknown key)
Key 0x11a (Unknown key)
Key 0x11b (Unknown key)
Key 0x11c (Unknown key)
Key 0x11d (Unknown key)
Key 0x11e (Unknown key)
Key 0x11f (Unknown key)
Buttons/Keys: 16

Supported Relative axes:
Relative axis 0x00 (X Axis)
Relative axis 0x01 (Y Axis)
Relative axis 0x06 (Horizontal Wheel)
Relative axis 0x08 (Vertical Wheel)


En fin bueno despues de romperme la cabeza pensando que faltaba instalar el driver evdev y buscando recompilar xorg con sdk y demas cosas, encontre buscando que ya tenia el bendito driver instalado... :) el genial Lenny ya lo tenia.

bueno siguiente paso solo modificar el archivo /etc/X11/xorg.conf

incluyendo las siguientes lineas.

Section "InputDevice"
Identifier "Configured Mouse"
Driver "evdev"
Option "CorePointer"
Option "Name" "Logitech USB Gaming Mouse"
Option "Device" "/dev/input/event2"
Option "ZAxisMapping" "4 5 6 7"
Option "Emulate3Buttons" "false"
EndSection


luego reiniciar el ordenador y ya esta funcionando... todos los botones.
bueno todavia me falta mapear a medida los botones del mouse lo cual incluire en el siguiente post.



otra solucion mucho mas simple es modificar este ultimo archivo /etx/X11/xorg.conf con lo siguiente

Section "InputDevice"
Identifier "Mouse0"
Driver "evdev"
Option "evBits" "+1-2"
Option "keyBits" "~272-287"
Option "relBits" "~0-2 ~6 ~8"
Option "Pass" "3"
EndSection


esto uiltimo lo obtuve aqui

sábado, 20 de junio de 2009

Ardour en DEBIAN Lenny con Audigy 2 ZS Notebook

Entre mis cosas pendientes tenia el configurar mi tarjeta de Sonido PC card Audigy 2 zs, siendo que mi primer intento de configurarla en Windows Vista fue infructuoso, pues no hay controlador adecuado. Al tener una partición dedicada enteramente a Debian. conecte la tarjeta y con sorpresa pude ver que Debian Lenny levanta esta tarjeta sin ningún problema tiene el controlador y todo.

Cuando quise escuchar no puede hacerlo apesar que se veia conectada se comprueba con

$ cat /proc/asound/cards
0 [Intel ]: HDA-Intel - HDA Intel
HDA Intel at 0xb0000000 irq 20
1 [Audigy2 ]: Audigy2 - Audigy 2 ZS Notebook [SB0530]
Audigy 2 ZS Notebook [SB0530] (rev.0, serial:0x20011102) at 0x2400, irq 20

Investigando puede averiguar que ALSA utiliza el dispositivo que se le indica como por defaul, asi como alsamixergui (este te sirve para saber que dispositivo esta funcionando). en este caso el 0.
Buscando por Internet encontre que se debe poner alguna configuracion para que arranque la Audigy por defaul. consistia en crear un archivo asound.conf y ponerlo en /etc/
con el siguiente texto. (archivo /etc/asound.conf)

$ nano /etc/asound.conf
pcm.!default {
type hw
card 1
}
ctl.!default {
type hw
card 1
}
donde el 1 le indica que utlice la Audigy por default. De esta manera pude escuchar por primera vez por mi tarjeta Audigy sonido almacenado se escucha muy bien.

Bueno el paso siguiente era utilizar la tarjeta para por ejemplo capturar sonido externo... ejem Guitarra, sonido etc. buscando un manejar de sonido al estilo SONAR u otro di a para con "Ardour"
excelente programa para grabaciones multitrack y para modificar grabaciones.

Ejecute Ardour y no pude conseguir nada, ni siquiera escuchar algo, otra vez a buscar informacion, dia enterarme que Ardour usa para manejar sonido un programa (servidor de sonido), llamado JACK. Luego probando JACK bastante pues al inicio al igual que alsa este programa solo ejecuta con la tarjeta interna de la laptop y no con la Audigy. Finalmente di a parar con un archivo que configura JACK (este archivo se sobre escribe al ejecutar y modificar opciones en qjackctl) se llama .jackdrc (archivo oculto!)... por cierto todo lo hice en consola root.

$nano /root/.jackdrc
/usr/bin/jackd -dalsa -dhw:1 -r44100 -p1024 -n8

Asi al arrancar qjackctl JACK utiliza la Audigy.

Hasta ahi todo bien.. luego aprendi que para utilizar el servidor de sonido JACK por ejemplo con alsaplayer se necesita un comando en la consola.

$alsaplayer -o jack

le dice al programa que ejecute con jack como servidor de sonido, es decir utilice la Audigy para reproducir, bueno asimismo. Ya que ardour utliza jack para sonido, Ardour tambien puede ver lo que alsaplayer este

- Conecciones para utilizar la tarjeta con ALsaplayer, jack, ardour, microfono.
qjackctl
alsaplayer -o jack
ardour2 &

jueves, 26 de marzo de 2009

MGMAPS

Hace poco adquirí un Nokia E71, luego de revisar las opciones probé el GPS, utilizando los programas incorporados entendí que para ubicarse el equipo utiliza las señales de satélites, usando para tal fin el GPS integrado y el A-GPS (GPS asistido), este último con costo por datos GPRS.
Al usar el GPS para posicionarme quise ver el mapa, así pude verificar que los mapas están en linea, y que para verlos se necesita conexión a internet, por wifi o data gprs.
Sin embargo que se hace donde no hay operador ni wifi?... entonces la idea de bajar los mapas fue aumentando. Navegando por internet fui a parar con el MGMAPS...

Lo demás es historia el procedimiento fue el siguiente:

1. Instalar MGMAPS archivos jar y jad (este último es indispensable pues registra el software en el telefono.. pues sino esta pidiendote a cada momento la lectura de datos de memoria y es incomodo). El MGMAPS, lee los mapas y maneja el GPS en el equipo.

2. Buscar el mapa y seleccionar las coordenadas a bajar usando la página Map Creation tool. Para seleccionar se presiona click derecho y izquierdo, creando una región. Luego se le dice que niveles (zoom, mientras mas es mas cerca. ejem. 17 es a 50mt, 16 es a 100mt etc) tipo de mapas Satellite with road maps overlayed. y Generate.
la herramienta crea un archivo *.map (texto plano) que contiene las coordenadas en formato raro (Lat. Inf Derecha, Long. Sup Izquierda : Lat.Sup Izquierda, Long. Inf Derecha)del área seleccionada, como se aprecia pueden haber varias áreas rectangulares para abarcar toda la región.

3. Bajar las areas que conforman la región con los datos del archivo map. con el programa, GMAPMAKER Utilizando la opción,
"Download tiles using entered Area", es decir indicando coordenadas obtenidas en el paso 2. y seleccionando zoom level de cada uno. del nivel 16 para abajo esta bien para mapas hybridos.

El MGMAPS, también cuenta con una herramienta para ver la posición del dispositivo mediante web con GMAP-track, el cual te muestra en tiempo real tu posicion actual, usando conexion a internet. GMAP-track no almacena los recorridos solo ubica la posición, en cada instante de muestreo.

martes, 20 de enero de 2009

Donde estaras - Grupo Grama

Ya pasado tanto tiempo
y no te puedo encontrar
los caminos de la vida
no me quieren ayudar.

Y mirando tu retrato
te recuerdo pienso en ti
reviviendo aquel momento
yo sigo

Donde estarás, donde andarás
en que otros brazos
mis recuerdos vivirás
las noches frías y oscuras
se recuestan junto a mi.
todo el tiempo te he soñado conmigo

Donde estarás, donde andarás
en que otros brazos
mis recuerdos vivirás
Los momentos que te he dado
no los quitaras de ti.
ni ese tiempo que yo he estado contigo

Fin del camino

lunes, 17 de noviembre de 2008

Por que te vas

Hoy en mi ventana brilla el sol y el corazón
se pone triste contemplando la ciudad
por que te vas.

Como cada noche desperté pensando en ti
y en mi reloj todas las horas vi pasar
por que te vas.

1. Todas las promesas de mi amor se irán contigo
me olvidaras, me olvidaras
junto a la estación no llorare igual que un niño
por que te vas, por que te vas.

Bajo la penumbra de un farol, se dormirán
todas las cosas que quedaron por decir
se dormirán

Junto a las manillas de un reloj esperaran
todas las horas que quedaron por vivir
esperaran.

lunes, 22 de septiembre de 2008