Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

miércoles, 5 de junio de 2019

Tutorial De Win32 Y Ejemplo Del Uso De Las Apis Bitblt, Getdibits Y Setdibits En Visual C++ 2008 Express


Este es otro tutorial viejito migrado desde la web Electrónica a Martillazos :D

En este tutorial la idea es manipular imágenes y mostrarlas en una ventana usando solamente apis de Windows. La manera más fácil es empleando el Visual C++ de Microsoft, el cual ya instala los archivos de encabezado necesarios y genera automáticamente el código para crear una ventana vacía.

Primero abrimos el Visual C++ (yo uso la versión 2008 Express), elegimos Nuevo Proyecto->Win32->Proyecto de Win32. A mi proyecto le he puesto el nombre de "FirstTry".

 

Como tipo de aplicación elegimos "Aplicación para Windows". Por ser la versión Express, no nos permite usar las librerías MFC ó ATL, pero no importa porque no las vamos a necesitar (y yo tampoco sé usarlas :P)




Al presionar el botón de "Finalizar", el Visual C++ nos crea varios archivos, con extensión .cpp y extensión .h. Donde debemos mirar es el código del archivo cpp que tiene el mismo nombre de nuestro proyecto. Para este caso es FirstTry.cpp. Con lo primero que nos encontramos es que el Visual C++ ha generado casi doscientas líneas de código. Todo ese código es necesario para mostrar una sola ventana, simple y vacía. Para entender en detalle qué significa, recomiendo este tutorial: The Forger's Win32 Api Tutorial.


Como resumen, voy a explicar un poco qué hace este código:


Hay cuatro funciones a las qué prestar atención: MyRegisterClass, InitInstance, WndProc y _tWinMain.


MyRegisterClass: En esta función se establecen las características de la ventana: colores, íconos, etc. También se le envía el nombre de la función (WndProc) donde se procesarán los eventos que ocurran en la ventana (al redimensionarla, maximizarla, hacerle click, cerrarla, etc).


InitInstance: Nuestro programa en ejecución (que por ahora sólo muestra una ventana vacía) se le conoce como instancia. Una aplicación puede tener varias instancias: por ejemplo, dos ventanas del Block de Notas abiertas son dos instancias de la aplicación Block de Notas. La función InitInstance inicializa la instancia actual de nuestra aplicación, crea la ventana llamando a la api CreateWindow y retorna un valor Verdadero o Falso dependiendo si la ventana pudo crearse con éxito o no.


WndProc: Aquí se procesa todo lo que ocurre en la ventana. Ese "todo lo que ocurre" se conoce como Eventos, y pueden ser desde entradas del teclado o clicks del ratón, hasta el cierre de la ventana y el final de la instancia de la aplicación.


_tWinMain: Función principal y desde dónde arranca nuestra aplicación. Esta función llama a MyRegisterClass y a InitInstance. Si todo sale bien, entrará al bucle de mensajes. Como en MyRegisterClass ya se asoció a la función WndProc con la ventana actual, no es necesario llamarla desde _tWinMain, de esto se encarga automáticamente el bucle de mensajes. Cuando ocurre un evento, la ejecución del programa "salta" a WndProc junto con un número que identifica al evento ocurrido.





Dentro de WndProc siempre hay un "switch" que evalúa el valor que acompaña a los eventos lanzados, de esta forma se puede identificar al evento y ejecutar el código correspondiente:




¿Complicado? ¡Claro que sí! Comparado con el Form_Load de Visual Basic o C#, esto es una pesadilla. Pero es así como el sistema operativo Windows maneja las ventanas de TODAS las aplicaciones. En Windows, no importa si se programó en .Net, VB6, Java o Borland C++, Delphi... las librerías que usan estos lenguajes no son más que "envolturas" (wrappers) de la api de Windows. En sus profundidades, todas las aplicaciones de Windows que tengan ventanas terminan en un bucle de mensajes.


Nota: Nótese que repito "Windows" muchas muchas veces, pues este tutorial es exclusivamente para este sistema operativo. En Linux las ventanas se crean con librerías como GTK, las cuales también poseen un bucle, pero las funciones y la lógica son totalmente distintas.


Para los ejemplos en este tutorial el evento que nos interesa es WM_PAINT, el cual se lanza cada vez que se maximiza o minimiza la ventana, cuando se la redimensiona, o se la vuelve a mostrar luego de estar tapada por otra ventana, en otras palabras, cada vez que se la "repinta".



Primer Ejemplo: Mostrar en la ventana una imagen en formato BMP.

Para cargar una imagen en el proyecto, debemos ir a donde dice Archivos de Recursos, hacer click derecho->Agregar->Elemento existente. Se abrirá una ventana desde la que elegiremos una imagen en formato BMP. En este caso he elegido un archivo llamado p1.bmp.




Luego le hacemos click derecho al archivo FirstTry.rc->Ver Código y añadimos la siguiente línea donde se declara el nombre con el que identificaremos a la imagen p1.bmp:




Luego vamos a Resources.h y definimos un valor único para el nombre que identifica a la imagen p1.bmp:


Estos pasos se deben repetir para cada imagen que se carga al proyecto. Para este ejemplo he cargado una segunda imagen llamada "IDI_CMC" y cuyo valor en Resources.h es 100.


A continuación vamos a FirstTry.cpp y, en la función WndProc agregamos el siguiente código en el "case" de WM_PAINT:



Este código crea un "handle" a la imagen ya definida en los archivos de recursos. Luego se crea un "Device Context" llamado hdcMem el cual se "enlaza" al handle de la imagen. Los "Device Context" (Dispositivos de Contexto) son áreas de memoria donde podemos dibujar y copiar imágenes. Luego se llama a la api GetObject para obtener las dimensiones de la imagen, éstos valores se guardarán en el objeto BITMAP llamado bm.


La imagen está cargada en algún lugar de la memoria RAM. Para que aparezca en la ventana debemos copiarla. Eso se hace con la api BitBlt cuyos parámetros son: destino (el handle del Device Context de la ventana, y que es el valor devuelto por la api BeginPaint y guardado en la variable hdc), coordenada "x" donde se ubicará la esquina superior izquierda de la imagen copiada, coordenada "y" donde se ubicará la esquina superior izquierda de la imagen copiada, ancho de la imagen, alto de la imagen, handle del Device Conext con la imagen a copiar, coordenada "x" de la esquina superior izquierda de la imagen a copiar, coordenada "y" de la esquina superior izquierda de la imagen a copiar, operación de copiado. Más información de esta api aquí.


Luego de terminar de utilizar los handles, bitmaps y demás objetos, se los debe borrar de la memoria. Al programar directamente con la Api de Windows usamos código no administrado. Es decir: no hay un recolector de basura que libere la memoria. Si una función en código no administrado no libera todos los recursos utilizados tendrá "fugas de memoria" (memory leaks) las cuales se irán acumulando cada vez que se llame a la función. En el peor de los casos terminará agotando la memoria asignada a nuestra aplicación y haciendo que se cuelgue.

Al ejecutar la aplicación se mostrará la imagen en la ventana. Cada vez que ésta se "repinte" ejecutará el código que vuelve a cargar y copiar la imagen. Si no fuera así, la imagen desaparecería si se minimiza y maximiza la ventana, se la redimensiona, etc.

El resultado final es:




Segundo Ejemplo: Copiar una imagen de forma que cubra toda la ventana.

Para tener el código de forma más ordenada, vamos a crear un nuevo archivo cpp y llamar sus funciones desde el "case" WM_PAINT en WndProc. Para ello hacemos click derecho en "Archivos de Código Fuente" y elegimos la opción Agregar->Nuevo Elemento:



En la ventana de "Agregar Nuevo Elemento" elegimos la opción Código->Archivo C++: 




Esto creará un archivo con extensión cpp en blanco. Primero le agregamos las referencias a los archivos de encabezado stafx.h y el que lleva el nombre del proyecto: 



Luego, en el archivo de encabezado con el nombre del proyecto, se añaden las declaraciones de las funciones que irán en el nuevo archivo cpp (para este caso lo he llamado Image.cpp). La función que dibujará y copiará una imagen en toda la ventana se llamará ImagenCopiar. En general, las funciones que dibujan en una ventana necesitan 3 parámetros: La instancia actual de la aplicación (para cargar la imagen dentro de un recurso), el handle de la ventana (hWnd) y el handle del Device Context (hdc) de la ventana (donde copiaremos la imagen).



A continuación muestro el código de ImagenCopiar. Buena parte del código simplemente se copió del ejemplo anterior, mas se han añadido la declaración de un objeto RECT y la llamada a la api GetWindowsRect; esto es para poder obtener las dimensiones de la ventana. Las dimensiones de la imagen se guardan en el objeto BITMAP llamado bm. Para este ejemplo he usado la imagen llamada p1.bmp declarada como IDB_BITMAP1.



El código incluye dos bucles anidados, los que recorren el ancho y el alto de la ventana en incrementos iguales al ancho y al alto de la imagen a copiar, la cual es mucho más pequeña que la ventana donde va a mostrarse. Dentro de los bucles se va alternado una llamada a la api BitBlt que copia exactamente la imagen a la ventana, y otra llamada a la misma api que copia la imagen pero con los colores invertidos. El resultado es:





Tercer Ejemplo: Manipular los píxeles de una imagen. Uso de las apis GetDIBits y SetDIBits.

Para este ejemplo declaramos una segunda función llamada ImagenManipular, la cual recibe los mismos parámetros que ImagenCopiar, convertirá la imagen p1.bmp a escala de grises y la mostrará en la ventana de la aplicación.




Para poder procesar individualmente cada píxel de p1.bmp, debemos extraerlos, guardarlos temporalmente en un array, realizar las operaciones necesarias con ellos, y devolverlos a una nueva imagen la cual se mostrará en la ventana.

Cada píxel de una imagen está compuesto de 3 ó 4 bytes que corresponden a los colores rojo, verde y azul, más el canal alpha que indica la transparencia, el cual no es usado en todos los formatos de imagen. En este ejemplo sólo se trabajará con los valores de los colores. Para el canal alpha existen otras apis como TransparentBlt ó AlphaBlend (la última es la más recomendable de usar) pero no nos ocuparemos de ellas en este ejemplo.


El código de ImagenManipular es el siguiente:




Usando la api GetDIBits extraemos los píxeles de la imagen, como esta api también necesita de una "cabecera" donde esté la información de la imagen (bits por píxel, dimensiones, compresión, etc), esta información la guardamos en las variables tipo BITMAPINFO, una para la imagen original y otra para la imagen ya convertida a escala de grises.. Las dimensiones de p1.bmp las obtenemos con la api GetObject. Leyendo tutoriales y otros códigos de ejemplo, supe que a las variables BITMAPINFO se les establece la altura de la imagen como un valor negativo ya que su sistema de coordenadas para la altura está invertido.


Las dos imágenes (la original y la que estará en escala de grises) necesitan su propio handle y su propio Device Context.

Para recorrer los píxeles de la imagen uso dos punteros: uno que siempre apuntará al inicio del array (buf2), y otro (buf) que lo recorrerá incrementando su valor. Luego coloco los píxeles en otra imagen mediante la api SetDIBits, la cual requiere también otra variable tipo BITMAPINFO para "construir" la nueva imagen en escala de grises.

SetDIBits crea (o "dibuja") la imagen en escala de grises en un espacio en memoria. Aún falta colocarla en la ventana para poder verla. Esto se hace con una simple copia usando la api BitBlt.


El resultado es:





Si llamo a la función ImagenCopiar y luego a ImagenManipular el resultado será:





Esto es porque ImagenManipular copia a p1.bmp encima de lo que ha sido dibujado por ImagenCopiar.


Una imagen cualquiera posee mucha más información que sólo los píxeles que la componen. Todos los archivos digitales poseen una cabecera que le dice al sistema operativo o a una aplicación qué tipo de archivo es. En el caso de imágenes, la cabecera almacena información de los bits por píxel, dimensiones, formato, tipo de compresión etc. E el caso de archivos jpg, también se almacena la Data Exif, que contiene información del dispositivo de captura (como una cámara digital), el software de edición, la fecha de creación, etc.  Las apis GetDIBits y SetDIBits nos permiten obtener sólo los píxeles de una imagen y guardarlos en un array para después procesarlos.


El proyecto con los tres ejemplos se puede descargar de aquí. Se compila con Visual C++ Express 2008.


Un detalle más: el código de estos ejemplos no está optimizado. Lo ideal es que las imágenes procesadas se guarden en un Device Context global y simplemente copiarlo al Device Context de la ventana cada vez que ésta se repinta.

sábado, 4 de mayo de 2019

Reemplazando Windows XP por Xubuntu 16 en una laptop del 2008

Mi vieja laptop Toshiba Satellite A215 tenía windows XP instalado y estaba teniendo problemas de compatibilidad (no podía reproducir videos de youtube, el antivirus se suicidó, y Visual Studio y Java me mostraron el dedo del medio) de modo que decidí darle una nueva vida con un Sistema Operativo nuevo. Escuché que Windows 7 es una buena opción para PCs que corren Windows XP, dicen que optimiza mejor el hardware y es totalmente compatible. He usado Windows 7 por años y lo amo, pero esta vez quise intentar algo diferente. En el trabajo usé Centos, Ubuntu y me enamoré de Linux Mint, pero como esta laptop es más vieja que mis zapatos tenía que buscar un Linux que le cupiera. Al final descargué Xubuntu 16 LTS desktop iso, porque no pude encontrar el iso de la versión 18 para 32 bits. Esta es una distro para computadoras con hardware humilde.

Estos son los pasos que debemos seguir:

0. ¡¡¡BACKUPEAMOS TODA LA DATA!!!
1. Descargamos el iso de Xubuntu (primera opción en el enlace).
2. Quemamos el iso en un DVD (puede ser regrabable) con infraRecorded. No se puede simplemente quemar el iso en el DVD, No va a funcionar. Con este software sólo requiere presionar el botón "Write Image" y hace todo el trabajo de crear un DVD booteable.
3. Reiniciamos la laptop desde el DVD.

4. Esperamos a que cargue:



5. Podemos probar Xubuntu si deseamos, esta opción no cambia nada en el sistema. Yo fui directamente a la instalación después de escoger el idioma:



6. Escogemos Instalar todos los codecs y presionamos "Continuar":





7. Vamos a "Más Opciones".
Como esta laptop tiene 3 particiones C, E y F, yo quise reescribir la partición C (donde el WinXP está) y dejar las otras intactas (si algo va mal, tengo backups).




8. Al presionar "Continuar" nos mostrará las particiones detectadas. La partición C es dev/sda2.




9. Escogemos la partición con Windows XP y le hacemos doble click:

Escogemos formatearla con el tipo de formato ext4 para la partición sda2 seleccionando la opción de "formatear la partición". ¡Cuidado de no tocar las otras dos particiones!



Debe quedar así:

Como la idea es reemplazar Windows XP por Xubuntu, le indico que el cargador de arranque esté también en /dev/sda2.


10. El instalador va a pedir una partición de intercambio. Escogí dev/sda1, era la partición de recuperación de Toshiba, pero para una máquina así de vieja no necesito recuperar nada. Esta opción está en la misma ventana del paso anterior. Finalmente mis particiones quedaron así:





¡Y ahora es el momento de la espera! La instalación copiará los archivos y configurará el sistema.


 De nada :D

Aquí el sistema nos dirá que saquemos el DVD y reiniciemos la máquina.

Funciona como la gloria, hasta reconoce las particiones NTFS:



No tocó mis otras particiones, mis archivos siguen ahí.

Unas cosas más para hacer:

sudo apt-get install gimp
sudo apt-get install firefox
sudo apt-get install firefox-locale-es


Esto es para instalar el editor de imágenes gimp, y actualizar el firefox en español.

:D :D :D :D :D

 El enlace del videíto es éste:


Update dos semanas después:
No me funciona el scanner.

Mi scanner es un CanoScan Lide 110 (debo haberlo comprado allá por el 2012) que funciona perfectamente con Windows 7 sin necesidad de instalar nada (utiliza el protocolo Windows Image Adquisition por defecto) y funcionaba perfectamente con el Windows XP.
Xubuntu 16 me lo reconoce, pero no puede escanear, lo más que logra hacer es intentar arrancar el motor del scanner y nada más.
Probé actualizando sane, xsane, easy-scan, reiniciando, añadiendo ppas raritos, actualizado con apt-get update y apt-get upgrade... y nada.

Bueno, esto queda pendiente, mientras tanto tengo otra laptop (del 2010, tengo la manía de encariñarme con mi hardware) con Windows 7.

Una última recomendación: antes de reemplazar alegremente un Windows por un Linux, es mejor asegurarse que todos los dispositivos, cacharros y demás funcionan con la distro que hemos elegido, nunca se sabe.

jueves, 11 de abril de 2019

Calculando el Conjunto de Wirt (entrada antigua)

(esta entrada estaba originalmente en Electrónica a Martillazos, como tener un hosting gratuito se volvió una pesadilla, he decidido copiar las viejas entradas aquí y mandar la web original al olvido. Como son entradas muy viejas, es posible que algunos links estén más muertos que Altavista, Enjoy!). 

Una implementación mejor está aquí.

En la página de PuntoPeek encontré el siguiente problema propuesto: "Calcular los n primeros números del conjunto de Wirth".

El conjunto de Wirth se define de la siguiente manera:
"El conjunto de Wirth es un subconjunto de los enteros positivos que cumple con la siguiente regla: el 1 pertenece al conjunto y si un numero k pertenece al conjunto entonces los números 2*k+1 y 3*k+1 pertenecen al conjunto."
La solución de PuntoPeek es mucho mejor. No calcula números extra.
Por mi parte, no hubiera seguido insistiendo con el problema del Conjunto de Wirth si no hubiera tenido la sensación que la solución es mucho, pero mucho más simple.

Para empezar me basé en la solución propuesta de PuntoPeek. Luego de analizarla a simple vista, noté que todo se reduce a:
1. En un array de tamaño fijo n ingreso el 1 como primer elemento,
2. Recorro todos los números naturales y le pregunto a cada uno: ¿eres el resultado de tomar un número k del array y realizar las operaciones: 3k+1 ó 2k+1? si es así, te añado al array.
3. Me detengo hasta completar todos los elementos del array.
Lo curioso es que el postulado del conjunto de Wirth hace pensar en una operación "y" (and lógica): "Si un número k es elemento del conjunto, también lo son los números 3k+1 y 2k+1", cuando en realidad es un or lógico: cada número k del conjunto genera 3k + 1 y 2k + 1. Entonces, para que otro número i sea del conjunto debe cumplir cualquiera de estas dos condiciones: i = 3k + 1 ó i = 2k + 1. O mejor dicho: (i-1)/3 ó (i-1)/2 debe darnos un número k que ya está en nuestro array..
Con el método de PuntoPeek se buscan los primeros números n preguntando si cumplen cualquiera de las dos condiciones. La variable k es conocida, pues ya está almacenada en el array.
En Python, el algoritmo es:


Uso datos de tipo float (coma flotante) para descartar los números que den resultados decimales (la variable i es en realidad un entero con representación flotante). Así evito falsos positivos o falsos rechazados por los errores que se introducen al redondear la división entre enteros.
El resultado es:



Lo interesante de este algoritmo es que sirve para generar cualquier serie de números que cumplan una condición. Basta cambiar los primeros elementos del array y la condición, pues lo que en realidad hace es extraer del conjunto de los números naturales todos aquellos números que cumplen una condición.
La serie Fibonacci se genera de la siguiente forma (como no implica divisiones, no necesito números de coma flotante):



Para las potencias de 2:



Para los factoriales (números que son el factorial de otro):




def Wirt(n):
    result = [1.0]

    i=1.0
    while len(result)<=n:
        i+=1.0
        if (i not in result and ((i-1)/2 in result or (i-1)/3 in result)):
            result.append(i)

    return [int(x) for x in result]

def Fib(n):
    result = [0, 1]

    i=0
    while len(result)<=n:       
        i+=1
        if i== result[-1] + result[-2]:
            result.append(i)       

    return result

def Pot2(n):
    result = []

    i=0
    p=0
    while len(result)<=n:       
        i+=1
        if i== 2**p:
            result.append(i)
            p+=1

    return result

def Fact(n):

    def fact(p):
        if p==1:
            return p
        else:
            return p*fact(p-1)

    result = []

    i=0
    p=1
    while len(result)<=n:       
        i+=1
        if i== fact(p):
            result.append(i)
            p+=1

    return result

print Wirt(50)
print Fib(10)
print Pot2(10)
print Fact(5)

 

Lo malo de este algoritmo: para el caso del factorial y las potencias de dos es lentísimo. Tarda horas (literalmente) en calcular potencias de 2 mayores a 2^20, y lo mismo sucede para factoriales mayores al factorial de 10. Ir preguntando a cada número si cumple una condición es muy ineficiente para series de números cuyos valores crecen de forma geométrica o exponencial.

Pero ha sido una buena práctica de aprendizaje :)

lunes, 1 de abril de 2019

Probando los SCRs

(esta entrada estaba originalmente en Electrónica a Martillazos, como tener un hosting gratuito se volvió una pesadilla, he decidido copiar las viejas entradas aquí y mandar la web original al olvido).

Sucedió que encontré varios SCRs ("Rectificadores Controlados de Silicio" como son los triacs y tiristores) los que ya daban por dañados. Después de tenerlos guardados algunos meses decidí probar si funcionaban o no. Al no tener plena confianza en el método de prueba con el multímetro que nos enseñó el profesor decidí buscar en internet otro método más confiable.
No lo encontré.
Bueno, había webs acerca de usar el multímetro para comprobar si los SCRs están operativos o no, pero cuando al aplicar estos métodos en los míos me salía que todos estaban dañados. Fue cuando perdí la paciencia y decidí que la mejor manera de probarlos de una vez por todas era armar el circuito básico con el que se los hace funcionar, y hacerlos funcionar.

El circuito es éste, y es de lo más sencillo:



Que no es más que el circuito básico de disparo de un SCR. En el caso de los tiristores, éstos poseen 3 terminales: ánodo ("A", la terminal positiva), cátodo ("K" o la terminal negativa) y la compuerta (Gate). 
Los SCRs funcionan como switches que se cierran si reciben un pulso de corriente a través de la compuerta Gate y seguirán permitiendo el paso de la corriente por las otras dos terminales aunque ya no haya corriente de compuerta (en la jerga de electrónica se dice que se quedan "clavados" o "enganchados", y a la corriente de compuerta se le llama "corriente de disparo"). Para saber la corriente mínima que se necesita para "disparar" el SCR, y la máxima que puede soportar, es necesario leer el datasheet. En mi caso usé los tiristores de la serie BT151 y triacs de la serie BT136.
Los Triacs también poseen 3 terminales: uno de ellos es la compuerta y los otros dos se llaman Terminal 1 y Terminal 2, siendo intercambiables entre sí pues un triac puede conducir la corriente en ambos sentidos (es muy útil si se emplea voltaje alterno), al contrario de un tiristor que sólo conduce en un sentido al igual que un diodo en polarización directa.
Con respecto al cátodo, un tiristor sólo puede dispararse si recibe una corriente de disparo positiva. Un triac puede dispararse con corriente tanto negativa como positiva (o corriente en ambos sentidos, considerando que un triac no tiene un cátodo propiamente dicho).
Para probar los triacs y tiristores usé el mismo circuito, lo único se que hace es intercambiar los SCRs (la ventaja de las series BT151 y BT136 es que tienen la compuerta en el mismo lugar).
El circuito que armé es el siguiente:


Aquí quiero recalcar que es mejor usar un pequeño motor DC a un led, la batería es de 9 voltios y eso es demasiado para un led (que generalmente soportan menos de 5 voltios) por lo que quemé un par de ellos al probar este circuito, incluso al ponerle la resistencia en serie con el led, ésta se calentaba bastante. Pero para fines de demostración está bien (y si no se les tiene mucho cariño a los leds, por supuesto). El valor de la resistencia depende de la corriente que entrega la fuente, para una corriente mínima 10 mA y considerando un led de 3 voltios (los más comunes) y una fuente de 9 voltios:

0.01=(9-3)/R
R=700 ohmios

El cual se considera un valor máximo para la resistencia. En la práctica resulta ser demasiado alto si se usa una pila o batería gastada como la que yo usé. A veces con 200 ohmios el led apenas se enciende. El valor de la resistencia se irá bajando hasta un valor adecuado, donde el led se encienda sin quemarse.
En el caso de un motor no es necesaria la resistencia. La fuente tampoco debe tener un valor muy pequeño ya que existe un voltaje mínimo que debe haber entre las otras dos terminales del SCR para que éste se dispare, en este punto también recomiendo leer el datasheet.
Para no hacer confusión al led (o el motor) se le llamara "carga" que es el nombre genérico de las cosas que se conectan a un circuito y que se desean hacer funcionar.
El cable cocodrilo rojo va conectado a la compuerta (cuidando que no toque las otras patas del SCR) y funciona como el switch que va del polo positivo de la fuente a la compuerta. Mientras esté desconectado la carga debe estar apagada o desactivada. Si se enciende significa que el SCR está cruzado y debe descartarse.
Luego lo que se hace es tocar por un instante (apenas un pequeño toque) el polo positivo de la fuente (en este caso la batería) con el cable cocodrilo rojo:



En este caso la carga debe activarse y quedarse activada aunque el cable cocodrilo rojo ya no esté tocando la fuente. Si esto sucede así el SCR se ha quedado "clavado" y significa que está en buen estado. Si la carga se apaga al quitar el cable cocodrilo rojo el SCR no "engancha" y también debe descartarse.
En el caso de un tiristor, si se intercambian el ánodo con el cátodo y se hace la prueba de disparo también se puede comprobar la polaridad

martes, 19 de febrero de 2019

Usando el viejo Conexión a Escritorio Remoto de Windows 7 en Windows 10

Windows 10 viene con su versión de Conexión a Escritorio Remoto, pero le faltan varias opciones de configuración, y a mí personalmente no me funcionaba el copiar/pegar archivos (a pesar que la pc remota también era Windows).
Felizmente el viejo Conexión a Escritorio Remoto que usaba en Windows7 sigue presente en Windows 10, pero escondido.

Primero nos vamos a inicio y tipeamos "mstsc":



Éste es el viejo Conexión a Escritorio Remoto, yo quise anclarlo al inicio:



Aquí se ve la diferencia entre La Conexión a Escritorio Remoto de Windows 10 con el heredado de Windows 7:



Me gusta La Conexión a Escritorio Remoto de Window 7 porque es muy personalizable, algo que le  falta a la versión de Windows 10:

 Con esta opción puedo copiar y pegar desde mi pc a la pc remota.


Presionando el botón "Más" se abre esta ventanita. Eligiendo esta opción puedo hacr que la pc remota vea mis discos duros locales como unidades de red compartidas.


Estas y otras opciones no las he encontrado en la versión para Windows 10. Si Microsoft las ha escondido, lo ha hecho muy bien.

viernes, 18 de enero de 2019

Usar el visualizador de imágenes antiguo de Windows en Windows 10

Tengo un Windows 10 bastante cutre, y extraño a rabiar el viejo visualizador de imágenes de Windows 7. Luego de leer varias webs supe que este visualizador aún existe en Windows 10, pero está escondido. Siguiendo los pasos de esta web https://www.tenforums.com/tutorials/14312-restore-windows-photo-viewer-windows-10-a.html he logrado usarlo aunque no como el visor de imágenes predeterminado, pero algo es algo :)

Primero hay que guardar el siguiente texto como un archivo de registro de windows (es decir, con extensión .reg):


Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll]

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell]

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\open]
"MuiVerb"="@photoviewer.dll,-3043"

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\open\command]
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,72,00,75,00,\
6e,00,64,00,6c,00,6c,00,33,00,32,00,2e,00,65,00,78,00,65,00,20,00,22,00,25,\
00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,46,00,69,00,6c,00,65,00,73,00,\
25,00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,20,00,50,00,68,00,6f,\
00,74,00,6f,00,20,00,56,00,69,00,65,00,77,00,65,00,72,00,5c,00,50,00,68,00,\
6f,00,74,00,6f,00,56,00,69,00,65,00,77,00,65,00,72,00,2e,00,64,00,6c,00,6c,\
00,22,00,2c,00,20,00,49,00,6d,00,61,00,67,00,65,00,56,00,69,00,65,00,77,00,\
5f,00,46,00,75,00,6c,00,6c,00,73,00,63,00,72,00,65,00,65,00,6e,00,20,00,25,\
00,31,00,00,00

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\open\DropTarget]
"Clsid"="{FFE2A43C-56B9-4bf5-9A79-CC6D4285608A}"

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\print]

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\print\command]
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,72,00,75,00,\
6e,00,64,00,6c,00,6c,00,33,00,32,00,2e,00,65,00,78,00,65,00,20,00,22,00,25,\
00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,46,00,69,00,6c,00,65,00,73,00,\
25,00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,20,00,50,00,68,00,6f,\
00,74,00,6f,00,20,00,56,00,69,00,65,00,77,00,65,00,72,00,5c,00,50,00,68,00,\
6f,00,74,00,6f,00,56,00,69,00,65,00,77,00,65,00,72,00,2e,00,64,00,6c,00,6c,\
00,22,00,2c,00,20,00,49,00,6d,00,61,00,67,00,65,00,56,00,69,00,65,00,77,00,\
5f,00,46,00,75,00,6c,00,6c,00,73,00,63,00,72,00,65,00,65,00,6e,00,20,00,25,\
00,31,00,00,00

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\print\DropTarget]
"Clsid"="{60fd46de-f830-4894-a628-6fa81bc0190d}"


Luego hay que hacerle al archivo .reg doble click y aceptar. Es probable que en casos como el mío este paso no sea necesario, pues mi Visor de Imágenes nunca apareció entre los visores predeterminados del sistema, así que para hallarlo y usarlo hay que realizar lo siguiente:

Click derecho sobre la imagen deeada, elegir "Abrir con" y elegir "Otra Aplicación":




En la ventana que nos aparece, escoger "Más Aplicaciones":



Y aquí está mi visor:






Y este es el resultado:




En mi caso, por más que selecciono este visor como la aplicación predeterminada para ver imágenes, Windows insiste en usar la aplicación Fotos, la cual no me gusta porque al minimizarla no mantiene el zoom que elijo para ver imágenes.

sábado, 27 de octubre de 2018

La Tortuga de Python

Buscando tutoriales, llegué a esta web:
http://elclubdelautodidacta.es/wp/2014/12/python-una-tortuga-de-brocha-fina/

Yo no sabía que Python tenía una tortuga al mismo estilo Logo. Luego de experimentar un poco con el código, llegué a esto:

import turtle

turtle.showturtle()
turtle.shape("turtle")

for i in range (0, 36):
    turtle.penup()
    turtle.left(10)
    turtle.forward(80)
    turtle.pendown()
    turtle.circle(50)
    turtle.penup()
    turtle.forward(-80)
   
turtle.mainloop()
turtle.done()
turtle.exitonclick()


El resultado es:



Haciendo unos pequeños cambios:

import turtle

turtle.showturtle()
turtle.shape("turtle")

for i in range (0, 36):
    turtle.penup()
    turtle.left(10)
    turtle.forward(25)
    turtle.pendown()
    turtle.circle(90)
    turtle.penup()
    turtle.forward(-25)
   

turtle.hideturtle()
turtle.mainloop()
turtle.done()
turtle.exitonclick()


Se obtiene:


Más info se puede hallar en la documentación oficial (en inglés).

martes, 18 de septiembre de 2018

El colador o la criba de Eratóstenes con Linq y recursivo

Ya antes había jugado con el colador de Eratóstenes, pero ahora toca hacerlo más pretencioso, menos legible y más recursivo :D
En total son tres intentos nada prácticos pero muy instructivos, pues voy a jugar un poco con funciones lambda recursivas:

// primer intento
Func<int, int> colador = null;
int[] nums = Enumerable.Range(2, 48).ToArray();

colador = n =>
{
    if (n == 8)
        return 0;
    else
    {
        nums = nums.Where(c => c == n || c % n != 0).ToArray();
        return colador(n + 1);
    }
};

colador(2);
nums.ToList().ForEach(c => Console.Write(c.ToString() + " "));

// el primer intento es una shit, modifica un array que está fuera de la función lambda.

// Acá viene el segundo intento:
Func<int, IEnumerable<int>, IEnumerable<int>> colador2 = null;
IEnumerable<int> nums2 = Enumerable.Range(2, 48);

colador2 = (n, x) =>
{
    if (n == 8)
        return x;
    else
    {
        x = x.Where(c => c == n || c % n != 0);
        return colador2(n + 1, x);
    }
};

nums2 = colador2(2, nums2);
Console.WriteLine();
nums2.ToList().ForEach(c => Console.Write(c.ToString() + " "));

// el segundo intento aún es demasiado legible.

// Acá va el tercer intento:
Func<int, IEnumerable<int>, IEnumerable<int>> colador3 = null;
IEnumerable<int> nums3 = Enumerable.Range(2, 48);

colador3 = (n, x) =>
{
    x = x.Where(c => c == n || c % n != 0);
    return (n < 8) ? colador3(n + 1, x) : x;
};

nums3 = colador3(2, nums3);
Console.WriteLine();
nums2.ToList().ForEach(c => Console.Write(c.ToString() + " "));

Console.ReadLine();



El resultado es:

viernes, 6 de julio de 2018

Cómo obtener información de la Distro de Linux

Más de una vez me ha pasado necesitar saber la versión y el nombre de una distro de Linux, y si es de 32 o 64 bits. Luego de probar con varios comandos, encontré dos que son fáciles de recordar:

>cat /etc/lsb-release
Me da el nombre y la versión de la distro.

>uname -a
Me dice si es de 32 o 64 bits.


A continuación muestro la información que dan estos comandos en distintos Ubuntus gracias a la magia de VirtualBox :D

Si dice "x86_64" es de 64 bits. Este Ubuntu vino con Unity.


Otra de 64 bits. Éste es Ubuntu Mate.


Este es un Ubuntu de 32 bits con xfce. Si es de 32 bits, uname sólo mostrará "i686".


Este es un Xubuntu 14 de 32 bits.


Debo añadir que estos comandos no dan información acerca del entorno de escritorio que se está usando.