Voltaje RMS de una sinusoidal en CA

Valor RMS

El valor RMS (Root Mean Square), valor cuadrático medio o valor eficaz se puede entender como el valor de tensión que producirá una disipación calorífica en una resistencia óhmica cuando fluye una corriente a través de esta, de forma equivalente a lo que produciría un valor en corriente continua (CC); es decir, este valor se considera como un valor constante a partir de una onda variable, lo cual simplifica los cálculos y permite hacer mediciones. Si bien es usado típicamente para formas de onda periódicas, también puede ser aplicado a cualquier forma de onda, sean estas constantes o no.

De acuerdo a su nombre, el valor RMS es la raíz cuadrada del promedio de los cuadrados de los valores instantáneos de la forma de onda, pudiendo establecerse a partir del voltaje RMS o Vrms.

Al valor RMS se le suele denominar "valor efectivo" (generalmente se representa con Veff), cuando el valor de la tensión RMS de una forma de onda sinusoidal alterna se usa para comparar la potencia eléctrica que se entrega a una carga determinada, desde un circuito DC equivalente.

Como ejemplo, en la red eléctrica de algunos países de europa se utilizan 220 VAC, esto indica un valor efectivo de "220 Voltios RMS", lo cual implica que la tensión RMS sinusoidal de las tomas de corriente son capaces de entregar la misma potencia media positiva, tal como lo harían 220 Voltios de tensión de CC constantes, tal y como se muestra a continuación:


Por lo tanto, de forma equivalente el voltaje DC que generaría la misma potencia sería:

Cálculo del valor RMS de una onda sinusoidal

De manera intuitiva se podría pensar que el promedio de los valores instantáneos de la forma de onda sinuosidal nos dan el valor rms; sin embargo, como ya se mencionó anteriormente, de acuerdo a sus siglas el valor rms no se calcula directamente como un valor promedio de la forma de onda sinusoidal, de lo contrario la parte positiva y la parte negativa en un ciclo de la onda se anularían, es decir, obtendríamos un valor promedio igual a cero, con lo cual la potencia obtenida también sería cero.

Para obtener el valor RMS de una onda sinusoidal, podemos hacer uso de dos métodos: El método analítico y el método gráfico.

Método analítico

Matemáticamente se puede calcular el valor del voltaje o corriente rms de una forma de onda periódica usando la expresión siguiente:
Entonces, para una onda con comportamiento periódico sinosuidal que se define a partir de la función f(t):
y usando la expresión de la definición anterior para hallar el Voltaje rms, se tiene:
Luego, por la identidad trigonométrica del ángulo doble:
Además, al realizar y evaluar la integración a través de los límites tomados para un período "T" o de 0° a 360° y donde, ω=2π/T, se obtiene:
Por lo que la expresión resultante es:
Finalmente, se demuestra que para una onda sinusoidal, el voltaje RMS no es más que el valor del voltaje pico (Vpico) de la misma divido entre √2 o lo que es lo mismo 0.7071xVpico.
Entonces, si por ejemplo se tiene una onda sinusoidal de amplitud 2 Vpico, el valor del voltaje eficaz será 1.4142 Voltios.

Método gráfico

El valor eficaz o RMS utilizando el método gráfico se puede hallar con razonable precisión, mediante una cantidad considerable de valores instantáneos igualmente espaciados a lo largo de la forma de onda. Para el cálculo del valor rms de una onda sinusoidal usando este método, se puede considerar únicamente la mitad de un período o medio ciclo positivo.

Entonces, se divide esta forma de onda en "n" segmentos de tal forma que se toman las amplitudes (eje de voltajes) para cada instante de división del eje temporal. Luego, se toma la raíz cuadrada al valor resultante de sumar los valores de las amplitudes elevadas al cuadrado y dividiendo dicha suma entre los n valores. Vale mencionar que a mayor cantidad de muestras n, se obtendrá mayor exactitud en el resultado final.
Por ejemplo, considerando el siguiente gráfico de una sinusoidal de medio ciclo positivo, con amplitud de 2 Vpico.
Además, en la siguiente tabla se muestran los valores de cada valor de amplitud asociado a su fase correspondiente.
Luego, se aplican los valores tomados del gráfico en la fórmula correspondiente, tal como se muestra de la siguiente forma:
Por lo que el resultado obtenido es 1.415 Voltios, valor muy cercano al resultante mediante el método analítico.

Finalmente, se debe mencionar que en la práctica existe instrumentación capaz de medir el valor eficaz, como por ejemplo el multímetro o el osciloscopio.

Threads en Java (Parte II)

Threads y prioridades

Aunque un programa utilice varios threads y aparentemente estos se ejecuten simultaneamente, el sistema ejecuta una sóla instrucción cada vez (esto es particularmente cierto en sistemas con una sola CPU), aunque realizado a velocidad suficiente para proporcionar la ilusión de simultaneidad. El mecanismo por el cual un sistema controla la ejecución concurrente de procesos se llama planificación (scheduling). Java soporta un mecanismo simple denominado planificacion por prioridad fija (fixed priority scheduling). Esto significa que la planificación de los threads se realiza en base a la prioridad relativa de un thread frente a las prioridades de otros.

La prioridad de un thread es un valor entero (cuanto mayor es el número, mayor es la prioridad), que puede asignarse con el método setPriority. Por defecto, la prioridad de un thread es igual a la del thread que lo creó. Cuando hay varios threads en condiciones de ser ejecutados (estado runnable), la máquina virtual elige el thread que tiene una prioridad más alta, que se ejecutará hasta que:
  • Un thread con una prioridad más alta esté en condiciones de ser ejecutado (runnable), o
  • El thread termina (termina su metodo run), o
  • Se detiene voluntariamente o
  • Alguna condición hace que el thread no sea ejecutable (runnable), como una operación de entrada/salida o, si el sistema operativo tiene planificación por división de tiempos (time slicing), cuando expira el tiempo asignado.
Si dos o más threads están listos para ejecutarse y tienen la misma prioridad, la máquina virtual va cediendo control de forma cíclica (round-robin).

El hecho de que un thread con una prioridad más alta interrumpa a otro se denomina se denomina "planificación con derecho preferente" (preemptive scheduling).

Cuando un thread entra en ejecución y no cede voluntariamente el control para que puedan ejecutarse otros threads, se dice que es un thread egoista (selfish thread). Algunos Sistemas Operativos, como Windows, combaten estas actitudes con una estrategia de planificación por división de tiempos (time-slicing), que opera con threads de igual prioridad que compiten por la CPU. En estas condiciones el Sistema Operativo asigna tiempos a cada thread y va cediendo el control consecutivamente a todos los que compiten por el control de la CPU, impidiendo que uno de ellos se apropie del sistema durante un intervalo de tiempo prolongado.

Este mecanismo lo proporciona el sistema operativo, no Java.

Sincronización de Threads

El ejemplo del capítulo anterior muestra un programa que ejecuta varios threads de forma asíncrona. Es decir, una vez que es iniciado, cada thread vive de forma independiente de los otros, no existe ninguna relación entre ellos, ni tampoco ningún conflicto, dado que no comparten nada. Sin embargo, hay ocasiones que distintos threads en un programa si necesitan establecer alguna relación entre sí o compartir objetos. Se necesita entonces algún mecanismo que permita sincronizar threads así como establecer unas 'reglas del juego' para acceder a recursos (objetos) compartidos.

Un ejemplo típico en que dos procesos necesitan sincronizarse, es el caso en que un thread produzca algún tipo de información que es procesada por otro thread. Al primer thread le denominaremos Productor y al segundo Consumidor. El Productor podría tener el siguiente aspecto:
public class Productor extends Thread {
    private Contenedor contenedor;

    public Productor (Contenedor c) {
        contenedor = c;
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            contenedor.put(i);
            System.out.println("Productor. put: " + i);
            try {
                sleep((int)(Math.random() * 100));
            } catch (InterruptedException e) { }
        }
    }
}
Productor tiene una variables miembro: contenedor es una referencia a un objeto Contenedor, que sirve para almacenar los datos que va produciendo. El método run genera aleatoriamente el dato y lo coloca en el contenedor con el método put. Después espera una cantidad de tiempo aleatoria (hasta 100 milisegundos) con el método sleep. Productor no se preocupa de si el dato ya ha sido consumido o no. Simplemente lo coloca en el contenedor.

El Consumidor, por su parte podría tener el siguiente aspecto:
public class Consumidor extends Thread {
    private Contenedor contenedor;

    public Consumidor (Contenedor c) {
        contenedor = c;
    }

    public void run() {
        int value = 0;
        for (int i = 0; i < 10; i++) {
            value = contenedor.get();
            System.out.println("Consumidor. get: " + value);
        }
    }
}
El constructor es equivalente al del Productor. El método run, simplemente recupera el dato del contenedor con el método get y lo muestra en la consola. Tampoco el consumidor se preocupa de si el dato está ya disponible en el contenedor o no.

Productor y Consumidor se usarían desde un método main de la siguiente forma:
public class ProducTorConsumidorTest {
    public static void main(String[] args) {
        Contenedor c = new Contenedor ();
        Productor produce = new Productor (c);
        Consumidor consume = new Consumidor (c);

        produce.start();
        consume.start();
    }
}
Simplemente se crean los objetos, contendor, productor y consumidor y se inician los threads de estos dos últimos.

La sincronización que permite a productor y consumidor operar correctamente; es decir, que hace que consumidor espere hasta que haya un dato disponible, y que productor no genere uno nuevo hasta que haya sido consumido esta en la clase Contenedor, que tiene el siguiente aspecto:
public class CubbyHole {
    private int dato;
    private boolean hayDato = false;

    public synchronized int get() {
        while (hayDato == false) {
            try {
                // espera a que el productor coloque un valor.
                wait(); }
            catch (InterruptedException e) { }
        }
        hayDato = false;
        // notificar que el valor ha sido consumido.
        notifyAll();
        return dato;
    }

    public synchronized void put(int valor) {
        while (hayDato == true) {
            try {
                // espera a que se consuma el dato.
                wait();
            } catch (InterruptedException e) { }
        }
        dato = valor;
        hayDato = true;
        // notificar que ya hay dato.
        notifyAll();
    }
}
La variable miembro dato es la que contiene el valor que se almacena con put y se devuelve con get. La variable miembro hayDato es un flag interno que indica si el objeto contiene dato o no.

En el método put, antes de almacenar el valor en dato hay que asegurarse de que el valor anterior ha sido consumido. Si todavía hay valor (hayDato es true) se suspende la ejecución del thread mediante el método wait. Invocando wait (que es un método de la clase Object) se suspende el thread indefinidamente hasta que alguien le envíe una "señal" con el método notify o notifyAll. Cuando esto se produce (veremos que el notify lo produce el método get) el método continua, asume que el dato ya se ha consumido, almacena el valor en dato y envia a su vez un notifyAll para notificar a su vez que hay un dato disponible.

Por su parte, el método get chequea si hay dato disponible (no lo hay si hayDato es false) y si no lo hay espera hasta que le avisen (método wait). Una vez ha sido notificado (desde el método put) cambia el flag y devuelve el dato, pero antes notifica a put de que el dato ya ha sido consumido, y por tanto se puede almacenar otro.

La sincronización se lleva a cabo usando los métodos wait y notifiAll.

Existe además otro componente básico en el ejemplo. Los objetos productor y consumidor utilizan un recurso compartido que es el objeto contenedor. Si mientras el productor llama al método put y este se encuentra cambiando las variables miembro dato y hayDato, el consumidor llamara al método get y este a su vez empezará a cambiar estos valores, que podrían producirse resultados inesperados (este ejemplo es sencillo pero fácilmente pueden imaginarse otras situaciones más complejas).

Interesa, por tanto que mientras se esté ejecutando el método put nadie más acceda a las variables miembro del objeto. Esto se consigue con la palabra synchronized en la declaración del método. Cuando la máquina virtual inicia la ejecución de un método con este modificador, adquiere un bloqueo en el objeto sobre el que se ejecuta el método que impide que nadie más inicie la ejecución en ese objeto de otro método, que también esté declarado como syncrhonized. En nuestro ejemplo, cuando comienza el método put se bloquea el objeto de tal forma que si alguien intenta invocar el método get o put (ambos son synchronized) quedará en espera hasta que el bloqueo se libere (cuando termine la ejecución del método). Este mecanismo garantiza que los objetos compartidos mantienen la consistencia.

Este método de gestionar los bloqueos implica que:
  • Es responsabilidad del programador pensar y gestionar los bloqueos (A veces es una pesada responsabilidad).
  • Los métodos synchronized son más costosos en el sentido de que adquirir y liberar los bloqueos consume tiempo (este es el motivo por el que no están sincronizados por defecto todos los métodos).
  • Conviene evitar en lo posible el uso de objetos compartidos. Resultan difíciles de manejar.

REFERENCIAS:       arrakis

Threads en Java (Parte I)

Qué es un Thread

La Máquina Virtual Java (JVM) es un sistema multi-thread. Es decir, es capaz de ejecutar varias secuencias de ejecución (programas) simultáneamente. La JVM gestiona todos los detalles, asignación de tiempos de ejecución, prioridades, etc., de forma similar a como gestiona un Sistema Operativo múltiples procesos. La diferencia básica entre un proceso de Sistema Operativo y un Thread en Java es que los Threads corren dentro de la JVM, que es un proceso del Sistema Operativo y por tanto comparten todos los recursos, incluida la memoria y las variables y objetos allí definidos. A este tipo de procesos donde se comparte los recursos se les llama a veces "procesos ligeros" (lightweight process).

Java da soporte al concepto de Thread desde el mismo lenguaje, con algunas clases e interfaces definidas en el package java.lang y con métodos específicos para la manipulación de Threads en la clase Object.

Desde el punto de vista de las aplicaciones los threads son útiles porque permiten que el flujo del programa sea divido en dos o más partes, cada una ocupándose de alguna tarea. Por ejemplo, un Thread puede encargarse de la comunicación con el usuario, mientras otros actuan en segundo plano, realizando la transmisión de un fichero, accediendo a recursos del sistema (cargar sonidos, leer ficheros ...), etc. De hecho, todos los programas con interface gráfico (AWT o Swing) son multithread porque los eventos y las rutinas de dibujado de las ventanas corren en un thread distinto al principal.

La Clase Thread

La forma más directa para hacer un programa multi-thread es extender la clase Thread, y redefinir el método run(). Este método es invocado cuando se inicia el thread (mediante una llamada al método start() de la clase thread). El thread se inicia con la llamada al método run y termina cuando éste termina. El siguiente ejemplo ilustra estas ideas:
public class ThreadEjemplo extends Thread {
    public ThreadEjemplo(String str) {
        super(str);
    }
    public void run() {
        for (int i = 0; i < 10 ; i++)
            System.out.println(i + " " + getName());
        System.out.println("Termina thread " + getName());
    }
    public static void main (String [] args) {
        new ThreadEjemplo("Pepe").start();
        new ThreadEjemplo("Juan").start();
        System.out.println("Termina thread main");
    }
}
Compile y ejecute el programa. La salida, será algo asi:
Termina thread main
0 Pepe
1 Pepe
2 Pepe
3 Pepe
0 Juan
4 Pepe
1 Juan
5 Pepe
2 Juan
6 Pepe
3 Juan
7 Pepe
4 Juan
8 Pepe
5 Juan
9 Pepe
6 Juan
Termina thread Pepe
7 Juan
8 Juan
9 Juan
Termina thread Juan
BUILD SUCCESSFUL (total time: 1 second)
Ejecute varias veces el programa. Verá que no siempre se ejecuta igual.

Notas sobre el programa:
  • La clase Thread está en el package java.lang. Por tanto, no es necesario el import.
  • El constructor public Thread(String str) recibie un parámetro que es la identificación del Thread.
  • El método run contiene el bloque de ejecución del Thread. Dentro de él, el método getName() devuelve el nombre del Thread (el que se ha pasado como argumento al constructor).
  • El método main crea dos objetos de clase ThreadEjemplo y los inicia con la llamada al método start(). (el cual inicia el nuevo thread y llama al método run()).
  • Observe en la salida el primer mensaje de finalización del thread main. La ejecución de los threads es asíncrona. Realiza la llamada al método start(), éste le devuelve control y continua su ejecución, independiente de los otros threads.
  • En la salida los mensajes de un thread y otro se van mezclando. La máquina virtual asigna tiempos a cada thread.

La Interface Runnable

La interface Runnable proporciona un método alternativo a la utilización de la clase Thread, para los casos en los que no es posible hacer que nuestra clase extienda la clase Thread. Esto ocurre cuando nuestra clase, que deseamos correr en un thread independiente, deba extender alguna otra clase. Dado que no existe herencia múltiple, nuestra clase no puede extender a la vez la clase Thread y otra más. En este caso, nuestra clase debe implantar la interface Runnable, variando ligeramente la forma en que se crean e inician los nuevos threads.

El siguiente ejemplo es equivalente al del apartado anterior, pero utilizando la interface Runnable:
public class ThreadEjemplo implements Runnable {
    public void run() {
        for (int i = 0; i < 5 ; i++)
            System.out.println(i + " " + Thread.currentThread().getName());
        System.out.println("Termina thread " + Thread.currentThread().getName());
    }
    public static void main (String [] args) {
        new Thread (new ThreadEjemplo() , "Pepe").start();
        new Thread (new ThreadEjemplo() , "Juan").start();
        System.out.println("Termina thread main");
    }
}
Observese en este caso que:
  • Se implanta la interface Runnable en lugar de extender la clase Thread.
  • El constructor que había antes no es necesario.
  • En el main, observe la forma en que se crea el thread. Esa expresión es equivalente a:
ThreadEjemplo ejemplo = new ThreadEjemplo();
Thread thread = new Thread ( ejemplo , "Pepe") ;
thread.start();
  • Primero se crea la instancia de nuestra clase.
  • Después se crea una instancia de la clase Thread, pasando como parámetros la referencia de nuestro objeto y el nombre del nuevo thread.
  • Por último, se llama al método start de la clase thread. Este método iniciará el nuevo thread y llamará al método run() de nuestra clase.
  • Por útlimo, obsérvese la llamada al método getName() desde run(). getName es un método de la clase Thread, por lo que nuestra clase debe obtener una referencia al thread propio. Es lo que hace el método estático currentThread() de la clase Thread.

El ciclo de vida de un Thread

El gráfico de la figura 1 resume el ciclo de vida de un thread en Java:

Figura 1. Ciclo de vida de un Thread en Java.

Cuando se instancia la clase Thread (o una subclase) se crea un nuevo Thread que está en su estado inicial ("New Thread" en el gráfico). En este estado es simplemente un objeto más. No existe todavía el thread en ejecución. El único método que puede invocarse sobre él es el método start.

Cuando se invoca el método start sobre el thread el sistema crea los recursos necesarios, lo planifica (le asigna prioridad) y llama al método run. En este momento el thread está corriendo.

Si el método run invoca internamente el método sleep o wait o el thread tiene que esperar por una operación de entrada/salida, entonces el thread pasa al estado "no runnable" (no ejecutable) hasta que la condición de espera finalice. Durante este tiempo el sistema puede ceder control a otros threads activos.

Por último, cuando el método run finaliza, el thread termina y pasa a la situación "Dead" (Muerto).

REFERENCIAS:       arrakis

Comentarios, documentación y convenciones de nombres en Java

Comentarios

En Java existen comentarios de línea con // y bloques de comentario que comienzan con /* y terminan con */. Por ejemplo:
// Comentario de una linea
/* comienzo de comentario
   continua comentario
   fin de comentario */

Comentarios para documentación

El JDK proporciona una herramienta para generar páginas HTML de documentación a partir de los comentarios incluidos en el código fuente. El nombre de la herramienta es javadoc. Para que javadoc pueda generar los textos HTML es necesario que se sigan unas normas de documentación en la fuente, que son las siguientes:
  • Los comentarios de documentación deben empezar con /** y terminar con */.
  • Se pueden incorporar comentarios de documentación a nivel de clase, a nivel de variable (dato miembro) y a nivel de método.
  • Se genera la documentación para miembros public y protected.
  • Se pueden usar tags para documentar ciertos aspectos concretos como listas de parámetros o valores devueltos. Los tags se indican a continuación.

Tipo de tag Formato Descripción
Todos @see
Permite crear una referencia a la documentación de otra clase o método.
Clases @version
Comentario con datos indicativos del número de versión.
Clases @author
Nombre del autor.
Clases @since
Fecha desde la que está presente la clase.
Métodos @param
Parámetros que recibe el método.
Métodos @return
Significado del dato devuelto por el método.
Métodos @throws
Comentario sobre las excepciones que lanza.
Métodos @deprecated
Indicación de que el método es obsoleto.

Toda la documentación del API de Java está creada usando esta técnica y la herramienta javadoc.

Una clase comentada

import java.util.*;

/** Un programa simple en Java.
 * Envía un saludo e indica que día es hoy.
 * @author Mundo Telecomunicaciones
 * @version 1
 */
public class HolaATodos {

    /** Unico punto de entrada.
     * @param args Array de Strings.
     * @return No devuelve ningun valor.
     * @throws No dispara ninguna excepcion.
     */
    public static void main(String [] args) {
        System.out.println("Hola a todos");
        System.out.println(new Date());
    }

}

Convenciones de nombres

ORACLE recomienda un estilo de codificación que es seguido en el API de Java y en este post, y que consiste en:
  • Utilizar nombres descriptivos para las clases, evitando los nombres muy largos.
  • Para los nombres de clases poner la primera letra en mayúsculas y las demás en minúsculas. Por ejemplo: Empleado
  • Si el nombre tiene varias palabras ponerlas todas juntas (sin separar con - o _) y poner la primera letra de cada palabra en mayúsculas. Por ejemplo: InstrumentoMusical.
  • Para los nombres de miembros (datos y métodos) seguir la misma norma, pero con la primera letra de la primera palabra en minúsculas. Por ejemplo: registrarOyente.
  • Para las constantes (datos con el modificador final) usar nombres en mayúsculas, separando las palabras con _ solamente.

REFERENCIAS:       arrakis