Modulacin de seales

Vicente González Ruiz

June 24, 2014

Contents

1 El modulador de seales
2 El espectro de una seal de audio
3 Operaciones en el dominio del tiempo y de la frecuencia
4 Modulacin = desplazamiento en la frecuencia del espectro
5 Usando threads para hacer el programa interactivo
6 Control de los parmetros mediante un programa externo
7 Clientes y servidores
8 La interaccin entre los clientes y el servidor
9 El cdigo de modulator.c
10 El cdigo de ModulatorControl.java
11 El cdigo de dc.c
12 Sobre la compilacin
 12.1 Compilacin de dc
 12.2 Compilacin de modulator and ModulatorControl
13 Ejemplos de utilizacin

En esta prctica vamos a utilizar los pipes de Unix para transmitir datos entre las aplicaciones arecord y aplay, realizando un procesamiento en tiempo real de la seal de audio transferida. En esta fase de procesamiento realizaremos una modulacin de la seal de audio.

1 El modulador de seales

Un modulador de seales es un sistema al que entra una seal (la seal a modular, la seal de datos original, la seal de audio) y del que sale otra seal (la seal modulada, la nueva seal de datos, la seal de audio modulada). Adems, normalmente se le especifica la frecuencia de la seal moduladora como un parmetro de entrada (frecuencia de la seal moduladora o portadora). Para ello vamos a utilizar los pipes de Unix para leer de la tarjeta de sonido una seal, modularla y a continuacin reproducirla. Este sistema queda construido escribiendo:

arecord -f cd -t raw | modulator | aplay -f cd -t raw

Ntese que sin el flag -t raw modularamos tambin la cabecera WAV, con el consiguiente deterioro de la misma.

Tambin veremos en esta prctica de qu manera es posible sintetizar una seal digital de audio sencilla mediante un programa que escriba dicha seal sobre la salida estndar. En concreto, generaremos una seal sinusoidal peridica de una determinada frecuencia. Este tipo de seales son tiles para hacer comprobaciones, como veremos, acerca del buen funcionamiento de otros programas de tratamiento de audio.

Bien. Como ya hemos dicho, vamos a generar los datos escribindolos sobre la salida estndar. De esta manera y usando un pipe, podremos escuchar nuestro generador se seales sinusoidales escribiendo:

dc | modulator | aplay -f cd -t raw

El programa dc genera a la salida estndar un valor constante (1000 por defecto). Si modulamos esta seal obtenenos la sinusoide tal y como se explica a continuacin.

2 El espectro de una seal de audio

Las seales de audio, como el resto de seales unidimensionales y dependientes del tiempo, pueden representarse tanto en el dominio del tiempo como en el dominio de la frecuencia. En el primero, una muestra de audio indica, para el instante de tiempo que referencia la muestra, la amplitud del sonido. En el segundo, un coeficiente especifica, para todo el tiempo que comprende la ventana de anlisis, la amplitud de la sinusoide de frecuencia igual al ndice del coeficiente, que dicho sonido contiene.

3 Operaciones en el dominio del tiempo y de la frecuencia

En el caso de las seales de audio, el dominio del tiempo y el dominio de la frecuencia son equivalentes y por tanto, una determinada operacin que se realiza en uno de los dominios siempre tiene una equivalente en el otro.

Un ejemplo de sto lo encontramos en el filtrado de seales. Cuando filtramos, en el dominio de la frecuencia lo que realizamos (atenuando unas determinadas frecuencias con respecto a otras) es multiplicar el espectro de la seal de audio (la que queremos filtrar) con el espectro de la funcin de transferencia de filtro (la funcin que resulta de hacer pasar a travs del filtro la funcin impulso unitario). Como sabemos, por el teorema de convolucin, multiplicar los espectros de dos seales es equivalente a convolucionar las transformadas inversas de dichos espectros en el dominio del tiempo, es decir, para filtrar una seal en el dominio del tiempo podemos convolucionar dicha seal con la respuesta del filtro a la funcin impulso unitario.

4 Modulacin = desplazamiento en la frecuencia del espectro

Por definicin, modular una seal es desplazar su espectro en el dominio de la frecuencia. Para conseguir dicho desplazamiento en ω0 Hz, desde el punto de vista de la teora de seales, podemos convolucionar el espectro (por tanto, en el dominio de la frecuencia) con la funcin impulso unitario de frecuencia ω0 Hz. Sea dicha funcin δ(ω0).

Aplicando de nuevo el teorema de convolucin, dicha convolucin sera equivalente a multiplicar la seal por la transformada inversa de la funcin impulso unitario δ(ω0). Por definicin, la funcin cuyo espectro es una funcin impulso, es una funcin exponencial compleja en el dominio del tiempo. Como esta funcin no existe realmente (fsicamente) podemos simularla utilizando una sinusoide que, aunque presenta dos impulsos, es perfectamente vlida al no poderse representar fsicamente las frecuencias negativas.

Por tanto, para desplazar el espectro de una seal de audio debemos multiplicar cada una de las muestras por una funcin sinusoidal de una determinada frecuencia. La cantidad de desplazamiento que sufra el espectro va a depender de la frecuencia de la funcin sinusoidal, que por cierto, se suele llamar en el contexto de la modulacin de seales seal moduladora y seal portadora en el contexto de la transmisin de seales.

5 Usando threads para hacer el programa interactivo

Los threads (hilos o hebras en castellano) se utilizan para ejecutar procesos concurrentes. Cuando se implementa un programa interactivo como el que nos atae, en el que hay un proceso que no cesa (el que modula) y que depende de un parmetro de entrada que puede cambiar a lo largo del tiempo (la frecuencia de la seal moduladora), los hilos son la solucin. Usaremos un segundo hilo para determinar este parmetro de entrada.

En todo programa escrito en C existe un thread por defecto, el que ejecuta secuencialmente todo el cdigo escrito en el programa. Cuando se desea utilizar otro thread ms hay que declararlo de forma explcita. Para ello debemos crear una estructura del tipo pthread_t que se declara en el fichero cabecera pthread.h. A continuacin, lanzaremos el nuevo hilo materializado a travs de una determinada funcin. Veremos un ejemplo concreto ms adelante en el cdigo que acompaa a esta prctica.

6 Control de los parmetros mediante un programa externo

Existen muchas formas de indicar el valor de los parmetros de entrada a un programa. La forma ms simple es hacerlo a travs de la lista de argumentos a la funcin main(). Por desgracia, esta forma no permite la modificacin de dichos parmetros una vez que la ejecucin ya ha comenzado.

Otra forma de introducir los parmetros es a travs de la entrada estndar, que por defecto, si no se usan los pipes, es el teclado del terminal. Esta opcin requiere, como es lgico, el uso de un hilo dedicado. Sin embargo, en esta prctica preferiremos utilizar la entrada y la salida estndar para transferir otros datos (como las seales de audio).

La alternativa que vamos a escoger es la de utilizar un programa externo que se comunica con modulator a travs de un socket. Dicho programa se llamar ModulatorControl y estar escrito en Java. El lenguaje de programacin Java es muy adecuado para disear interfaces de usuario grficos lo que nos ayudar a controlar la frecuencia de la seal con comodidad.

Por otra parte, los sockets permiten que los programas modulator y ModulatorControl se ejecuten de forma independiente en hosts (ordenadores) distintos. As, podemos estar modulando la seal en un ordenador sin teclado ni pantalla (aunque s con tarjeta de sonido, como es lgico) y controlando la frecuencia de la seal portadora desde otro ordenador con un terminal grfico.

7 Clientes y servidores

En la jerga usada en redes de datos, un servidor representa generalmente una aplicacin de computadora que atiende a las peticiones realizadas por una o varias aplicaciones clientes. En esta prctica vamos a intentar que uno o varios clientes controlen de forma simultanea la frecuencia de la portadora que genera el servidor.

Para realizar la comunicacin desde los clientes al servidor vamos a usar el TCP que es ms fiable que el UCP y adems permite conectarnos al servidor usando programas estndar como el telnet. Cuando se usa el TCP antes de transmitir nada es neceario realizar una conexin, luego transmitiremos y finalmente es necesario realizar una desconexin. El tiempo que puede transcurrir desde que se realiza la conexin hasta que se produce la desconexin es, a priori, indeterminado. Esto significa que si un cliente se conecta al servidor y ste no puede atender a otro cliente mientras el primero no ha cerrado su conexin, el segundo cliente tardar un tiempo indefinido en ser atendido. Para evitar este problema se utiliza un tipo de servidor que se conoce como concurrente. Un servidor concurrente permite que muchos clientes permanezcan conectados el tiempo que sea necesario, realizndose los ciclos de conexin-transmisin-desconexin de una forma totalmente asncrona. La mayora de los servidores de datos que existen y que usan el TCP (como los servidores Web) son servidores concurrentes. Para atender concurrentemente a cada cliente utilizaremos un thread para cada uno de ellos.

Para enviar la frecuencia desde el servidor a los clientes vamos a usar el UDP porque permite realizar transmisiones multicast. En una transmisin de este tipo una nica fuente puede enviar un nico paquete de datos a muchos destinos usando la infraestructura existente en la red de transmisin. As, por muchos clientes que existan el servidor no se conviertir en el cuello de botella del sistema.

8 La interaccin entre los clientes y el servidor

El fujo de datos desde los clientes al servidor se realiza usando el TCP y por tanto hay que establecer una conexin por cada cliente. Dichas conexiones permanecern abiertas todo el tiempo que deseen los clientes. Cuando se reciba una frecuencia se enviar por el canal multicast dicha frecuencia a todos los clientes que escuchan en dicho canal. El servidor no sabe cuntos clientes existen (aunque podra hacer una suposicin razonable contando el nmero de conexiones TCP abiertas). Las frecuencias se codificarn en ASCII, para que pueda usarse el programa telnet como cliente. Para indicar el final de una conexin el cliente enviar una frecuencia negativa (una frecuencia imposible) al servidor.

A continuacin aparece un resumen de la interaccin cliente-servidor en seudo-cdigo:

Clientes                        Servidor  
------------------------------  ----------------------------------  
Conectar                        Esperar conexin  
Mientras conexin abierta:      Mientras frecuencia recibida >= 0:  
  Recibir frecuencia              Enviar frecuencia  
  Enviar frecuencia               Recibir frecuencia  
Enviar frecuencia negativa  
Cerrar conexin                 Cerrar conexin

Nota: la recepcin es bloqueante, es decir, mientras no se reciben los datos esperados la ejecucin est detenida.

9 El cdigo de modulator.c

Bueno. Ahora veremos un ejemplo concreto de cmo se puede escribir en C todo lo que hemos descrito en las secciones anteriores. Antes de compilar este cdigo, deberemos copiarlo en un fichero llamado modulator.c. Este ser nuestro servidor.

 
/* 
 * modulator.c 
 * 
 * Lee desde la entrada estndar una seal de audio (digital), a 16 bits 
 * por muestra, usando el endian de la mquina. Modula la seal a 
 * partir de una frecuencia especificada por uno o ms clientes que la 
 * especifican a travs de un socket TCP. Finalmente escribe la seal 
 * modulada en la salida estndard. 
 * 
 * gse. 2006. 
 * 
 */ 
 
/****************************************************************************** 
 * 
 * Ficheros cabecera. 
 * 
 *****************************************************************************/ 
 
/* Entrada y salida de streams buffereados. */ 
#include <stdio.h> 
 
/* Biblioteca de funciones estndar (exit(), EXIT_SUCCESS, 
   EXIT_FAILURE, ...) */ 
#include <stdlib.h> 
 
/* Manipulacin de cadenas y movimiento de memoria (memset(), ...). */ 
#include <string.h> 
 
/* Biblioteca estndar de funciones relacionadas con el sistema 
   operativo Unix (read(), write(), ...). */ 
#include <unistd.h> 
 
/* Biblioteca estndar de clculos matemticos en punto flotante 
   (sin(), rint(), ...).*/ 
#include <math.h> 
 
/* POSIX Threads (pthread_t, pthread_create(), ...). */ 
#include <pthread.h> 
 
/* Sockets. */ 
/* Ms info en: 
 * 
 * http://www.csce.uark.edu/~aapon/courses/os/examples/concurrentserver.c 
 * http://beej.us/guide/bgnet/ 
 */ 
 
/* Tipos de datos primitivos del sistema. */ 
#include <sys/types.h> 
 
/* Sockets (socket(), listen(), ...). */ 
#include <sys/socket.h> 
 
/* Direcciones IP, opciones y definiciones. */ 
#include <netinet/in.h> 
 
/* Servicio de resolucin de nombres. */ 
#include <netdb.h> 
 
/* Signals (interrupciones) (signal(), SIGINT). */ 
#include <signal.h>  /* signal(), SIGINT */ 
 
/* Manejo de parmetros desde la lnea de comandos. */ 
#include <getopt.h> 
 
/****************************************************************************** 
 * 
 * Definiciones. 
 * 
 *****************************************************************************/ 
 
/* Nmero de bytes del buffer de audio. */ 
#define BUFFER_SIZE 32 
 
/* Nmero de muestras por segundo (calidad CD). */ 
#define SAMPLES_PER_SECOND 44100 
 
/* Frecuencia por defecto en Hz. */ 
#define DEFAULT_FREQ 440 
 
/* Dos canales. */ 
#define NUMBER_OF_CHANNELS 2 
 
/* Puerto usado para atender a los clientes. */ 
#define LISTENING_PORT 6789 
 
/* Puerto destino de los paquetes UDP enviados a la direccin de broadcast. */ 
#define MULTICAST_PORT 6789 
 
/* Direccin multicast por defecto. Todos los clientes escuchan dicha 
   direccin multicast. */ 
#define MULTICAST_ADDR (char *)"224.0.0.1" 
 
/* Nmero mximo de clientes que esperan a que la conexin sea 
   establecida. */ 
#define QLEN 1 
 
/****************************************************************************** 
 * 
 * Variables globales. 
 * 
 *****************************************************************************/ 
 
/****************************************************************************** 
 * 
 * Funciones. 
 * 
 *****************************************************************************/ 
 
/* Cuerpo principal del programa. */ 
int main(int argc, char *argv[]) { 
  work(argc, argv); 
  return EXIT_SUCCESS; 
} 
 
/* Funcin que realiza todo el trabajo. */ 
work(int argc, char *argv[]) { 
 
  /* Si pulsamos CRTL+C, el programa acaba ejecutando la funcin 
     end(). */ 
  void end() { 
    fprintf(stderr,"%s: CTRL+C detected. Exiting ... ",argv[0]); 
    fprintf(stderr,"done\n"); 
    exit(EXIT_SUCCESS); 
  } 
  signal(SIGINT, end); 
 
  /* Un semforo para acceder "en exclusin mutua" a una zona de 
     cdigo. */ 
  pthread_mutex_t mut; 
 
  /* Creamos el semforo. */ 
  pthread_mutex_init(&mut, NULL); 
 
  /* Frecuencia de la seal sinusoidal en Hz. */ 
  int freq = DEFAULT_FREQ; 
 
  /* Puerto de escucha de los clientes. */ 
  short listening_port = LISTENING_PORT; 
 
  /* Puerto destino de los datagramas multicast. */ 
  short multicast_port = MULTICAST_PORT; 
 
  /* Canal multicast destino. */ 
  char *multicast_addr = MULTICAST_ADDR; 
 
  /* Manipulacin de los parmetros de entrada. */ { 
    int c; 
    while(1) { 
 
      static struct option long_options[] = { 
        {"frequency", required_argument, 0, f’}, 
        {"listening_port", required_argument, 0, l’}, 
        {"multicast_addr", required_argument, 0, a’}, 
        {"multicast_port", required_argument, 0, m’}, 
        {"help", no_argument, 0, ’?’}, 
        {0, 0, 0, 0} 
      }; 
 
      int option_index = 0; 
 
      c = getopt_long(argc, argv, "f:l:a:m:?", long_options, &option_index); 
      if(c==-1) { 
        /* No more options. */ 
        break; 
      } 
 
      switch (c) { 
      case 0: 
        /* If this option set a flag, do nothing else now. */ 
        if (long_options[option_index].flag != 0) 
          break; 
        fprintf(stderr, "option %s", long_options[option_index].name); 
        if (optarg) 
          fprintf(stderr, " with arg %s", optarg); 
        fprintf(stderr, "\n"); 
        break; 
 
      case f’: 
        freq = atoi(optarg); 
        break; 
 
      case l’: 
        listening_port = atoi(optarg); 
        break; 
 
      case a’: 
        multicast_addr = optarg; 
        break; 
 
      case m’: 
        multicast_port = atoi(optarg); 
        break; 
 
      case ’?’: 
        fprintf(stdout, 
                " -[-f]requency = frequency (in Hz) of the modulating signal (%d)\n", 
                freq); 
        fprintf(stdout, 
                " -[-l]istening port = port to serve the ModulatorControl clients (%d)\n", 
                listening_port); 
        fprintf(stdout, 
                " -[-]multicast_[a]ddress = multicast address to comunicate "); 
        fprintf(stdout, 
                "to the clients a change in the value of the frequency (%s)\n", 
                multicast_addr); 
        fprintf(stdout, 
                " -[-]multicast_[p]ort = port to which the multicast "); 
        fprintf(stdout, 
                "packets are sent (%d)\n", 
                multicast_port); 
        exit(1); 
 
      default: 
        fprintf(stderr, "%s: Unrecognized argument. Aborting ...\n", argv[0]); 
        abort(); 
      } 
    } 
  } 
 
  fprintf(stderr, "%s: frequency = %d Hz\n" ,argv[0], freq); 
  fprintf(stderr, "%s: listening port = %d\n", argv[0], listening_port); 
  fprintf(stderr, "%s: multicast address = %s\n", argv[0], multicast_addr); 
  fprintf(stderr, "%s: multicast port = %d\n", argv[0], multicast_port); 
 
  /* Hilo que realiza la sntesis de la sinusoide. */ 
  void *modu(void *arg) { 
 
    /* Angulo de la sinusoide, en radianes. */ 
    double angle_in_radians = 0; 
 
    /* La sinusoide ser generada hasta pulsar CTRL+C. */ 
    for(;;) { 
      int i; 
 
      /* El buffer de audio. */ 
      short buffer[BUFFER_SIZE]; 
 
      /* Leemos la entrada estndar. */ 
      int bytes_read; 
      bytes_read = read(0/* stdin */, buffer, BUFFER_SIZE*sizeof(buffer[0])); 
 
      /* Nmero de muestras ledas (sumando ambos canales). */ 
      int samples = bytes_read / sizeof(buffer[0]); 
 
      //fprintf(stderr,"%d\n",samples); 
 
      /* Media del canal izquierdo y derecho. */ 
      double ave_l = 0.0, ave_r = 0.0; 
      for(i=0; i<samples; i+=2) { 
        ave_l += buffer[i]; 
        ave_r += buffer[i+1]; 
      } 
      ave_l /= samples; 
      ave_r /= samples; 
 
      //fprintf(stderr,"*%f %f\n",ave_l, ave_r); 
 
      /* Eliminamos la componente continua en cada canal. */ 
      for(i=0; i<samples; i+=2) { 
        buffer[i] -= ave_l; 
        buffer[i+1] -= ave_r; 
      } 
 
      ave_l = 0; ave_r = 0; 
      for(i=0; i<samples; i+=2) { 
        ave_l += buffer[i]; 
        ave_r += buffer[i+1]; 
      } 
      ave_l /= samples; 
      ave_r /= samples; 
 
      //fprintf(stderr,"%f %f\n",ave_l, ave_r); 
 
      /* Modulamos. */ 
      for(i=0; i<samples; i+=2) { 
 
        /* Calculamos la portadora. */ 
        double portadora = sin(angle_in_radians); 
 
        /* Multiplicamos el valor de cada canal por el valor de la 
           portadora. */ 
        buffer[i] *= portadora; 
        buffer[i+1] *= portadora; 
 
        /* Angulo de la siguiente muestra de la portadora. */ 
        angle_in_radians += 2*M_PI*freq/SAMPLES_PER_SECOND; 
      } 
 
      /* Escribimos buffer en la salida estndar. */ 
      write(1/* stdout */, (void *)buffer, bytes_read); 
    } 
 
    /* Finalizamos el thread. */ 
    pthread_exit(0); 
 
  } 
 
  /* Lanzamos el hilo anterior. */ 
  pthread_t modu_tid; 
  if(pthread_create(&modu_tid, NULL, modu, NULL) == -1) { 
    perror("pthread_create"); 
    exit(EXIT_FAILURE); 
  } 
 
  /* Creamos el socket TCP de escucha. */ 
  int listen_sd; { 
 
    /* Obtenemos el nmero del protocolo. */ 
    struct protoent *ptrp; /* Pointer to a protocol table entry. */ 
    ptrp = getprotobyname("tcp"); 
    if((long)(ptrp) == 0) { 
      perror("getprotobyname"); 
      exit(EXIT_FAILURE); 
    } 
 
    /* Creamos el socket. */ 
    listen_sd = socket (PF_INET, SOCK_STREAM, ptrp->p_proto); 
    if(listen_sd < 0) { 
      perror("socket"); 
      exit(EXIT_FAILURE); 
    } 
 
    /* Usaremos el puerto de servicio sin esperar a que ste est 
       libre. */ 
    int yes = 1; 
    if(setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { 
      perror("setsockopt"); 
      exit(EXIT_FAILURE); 
    } 
  } 
 
  /* Asignamos una direccin (dir IP, puerto) al socket de escucha. */ { 
    struct sockaddr_in sad;              /* Direccin del servidor. */ 
    memset((char  *)&sad,0,sizeof(sad)); /* Borramos la estructura. */ 
    sad.sin_family = AF_INET;            /* Usaremos Internet. */ 
    sad.sin_addr.s_addr = INADDR_ANY;    /* Cualquiera de las IP 
                                            asignadas al host vale. */ 
    sad.sin_port = 
      htons((u_short)LISTENING_PORT);    /* Asignamos el puerto de escucha. */ 
    if (bind(listen_sd, (struct sockaddr *)&sad, sizeof (sad)) < 0) { 
      perror("bind"); 
      exit(EXIT_FAILURE); 
    } 
  } 
 
  /* Comenzamos a escuchar. */ 
  if (listen(listen_sd, QLEN) < 0) { 
    perror("listen"); 
    exit(EXIT_FAILURE); 
  } 
 
  /* Creamos el socket UDP. */ 
  int info_sd; 
  if((info_sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 
    perror("socket"); 
    exit(EXIT_FAILURE); 
  } 
 
  /* Dir IP y puerto del socket. */ 
  struct sockaddr_in info_addr; { 
    /* Usaremos Internet. */ 
    info_addr.sin_family = AF_INET; 
    /* IP multicast destino de los datagramas. */ 
    info_addr.sin_addr.s_addr = inet_addr(multicast_addr); 
    /* Puerto destino de los datagramas. */ 
    info_addr.sin_port = htons(multicast_port); 
    memset(&(info_addr.sin_zero), ’\0’, 8); 
  } 
 
  //fprintf(stderr,"%s: Frequency value = \"%d\"\n",  argv[0], freq); 
 
  /* Lazo del servidor. */ 
  while(1) { 
 
    fprintf(stderr,"%s: waiting for a connection ...\n", argv[0]); 
 
    /* Socket para "servir" a los clientes. */ 
    long serve_sd; 
    /* Esperamos a que un cliente se conecte. */ { 
      struct sockaddr_in cad;        /* Clients address. */ 
      socklen_t alen = sizeof(cad);  /* Tamao de la direccin. */ 
      serve_sd = accept(listen_sd, (struct sockaddr *)&cad, &alen); 
      if(serve_sd<0) { 
        perror("accept"); 
        exit (EXIT_FAILURE); 
      } 
    } 
 
    /* Hilo que controla a un cliente. */ 
    void *service(void *arg) { 
 
      /* El socket de conmunicacin con el cliente. Ntese que cada 
         cliente posee una variable "sd" diferente. */ 
      long sd = (long)arg; 
 
      /* Mientras la frecuencia recibida sea >= 0. */ 
      for(;;) { 
        int new_freq; 
        receive_from_client(sd, &new_freq); 
        if(new_freq<0) break; 
 
        /* Seccin crtica. */ 
        pthread_mutex_lock(&mut); 
        freq = new_freq; 
        fprintf(stderr,"%s: Frequency value = \"%d\"\n",  argv[0], freq); 
        pthread_mutex_unlock(&mut); 
        /* Fin de la seccin crtica. */ 
 
        inform_to_client(info_sd, info_addr, freq); 
      } 
 
      /* Cerramos la conexin con el cliente. */ 
      close(sd); 
 
      /* Finalizamos el thread. */ 
      pthread_exit(0); 
    } 
 
    /* Lanzamos el hilo. */ 
    pthread_t service_tid; /* Thread identificator. */ 
    if(pthread_create(&service_tid, NULL, service, (void *)serve_sd) == -1) { 
      perror("pthread_create"); 
      exit(EXIT_FAILURE); 
    } 
  } 
 
  /* Cerramos el socket de escucha (cuando pulsamos CTRL+C). */ 
  close(listen_sd); 
} 
 
/* Enva un paquete UDP con la frecuencia. */ 
inform_to_client(int sd, struct sockaddr_in addr, int freq) { 
  unsigned char message[2]; 
  //fprintf(stderr,"--> %d\n",freq); 
  message[0] = freq/256;//((freq & 0x000F) >>  0); 
  message[1] = freq%256;//((freq & 0x00F0) >>  8); 
  //message[2] = 3;//((freq & 0x0F00) >> 16); 
  //message[3] = 4;//((freq & 0xF000) >> 24); 
  sendto(sd, message, 2, 0,(struct sockaddr *) &addr, sizeof(addr)); 
} 
 
/* Recibe del cliente la nueva frecuencia. */ 
receive_from_client(int sd, int *freq) { 
  char message[256]; 
  read_text_line(sd, message); 
  sscanf(message,"%d",freq); 
} 
 
/* Lee una lnea de texto ASCII. */ 
read_text_line(int sd, char *str) { 
  int n; 
  do { 
    n = recv(sd, str, 1, 0); 
  } while (n>0 && *str++ != ’\n’); 
}

10 El cdigo de ModulatorControl.java

 
/* 
 * Crea una ventana redimensionable en la que aparece un JSlider y un 
 * JFormatterdTextField. El slider se utiliza para indicar la 
 * frecuencia usando el raton. El textField para indicarla 
 * numericamente. Ambos objetos estan sincronizados, es decir, si 
 * modificamos el estado de uno automaticamente se modifica el estado 
 * del otro. 
 * 
 * Esta clase contiene ademas un thread que escucha un canal multicast 
 * para conocer la frecuencia establecida en el servidor por otro 
 * objeto FreqControl. Cuando se recibe una frecuencia distinta de la 
 * actual, el slider y el textField son actualizados. 
 * 
 * La comunicacion con el servidor se realiza mediante las clases 
 * externas OutToServer y InFromServer. 
 * 
 * gse. 2006. 
 */ 
 
/* Evento de manipulacion de excepciones de entrada/salida. */ 
import java.io.IOException; 
 
/* En Java, un JFrame es una ventana independiente. */ 
import javax.swing.JFrame; 
 
/* Organiza objetos graficos en una ventana. */ 
import javax.swing.JPanel; 
 
/* Un campo con texto que cuando se modifica genera un evento. */ 
import javax.swing.JFormattedTextField; 
 
/* Permite construir "potenciometros" lineales. */ 
import javax.swing.JSlider; 
 
/* Etiquetas de texto */ 
import javax.swing.JLabel; 
 
/* Permite organizar unos objetos graficos con respecto a otros. */ 
import javax.swing.BoxLayout; 
 
/* Genera un evento cuando pulsamos una tecla. */ 
import javax.swing.KeyStroke; 
 
/* Una clase abstracta para los objetos que tratan con acciones. Las 
 * clases abstractas no pueden ser instanciadas, solo pueden ser 
 * heredadas. */ 
import javax.swing.AbstractAction; 
 
/* Da un formato concreto a un numero. Por ejemplo, esta clase se 
 * puede utilizar para hacer que aparezca el punto del millar ... */ 
import javax.swing.text.NumberFormatter; 
 
/* Establece un borde alrededor de un objeto grafico. */ 
import javax.swing.BorderFactory; 
 
/* Define un objeto que escucha los eventos de cambio. */ 
import javax.swing.event.ChangeListener; 
 
/* Se utiliza para avisar a otros objetos interesados en que ha 
 * cambiado el estado en una fuente de eventos. */ 
import javax.swing.event.ChangeEvent; 
 
/* Espeficica que se ha ocurrido un evento en un componente. */ 
import java.awt.event.ActionEvent; 
 
/* Escucha a los eventos de ventana. */ 
import java.awt.event.WindowListener; 
 
/* Es una clase que contiene una serie de metodos independientes de la 
 * plataforma (por ejemplo, el que genera un "beep"). */ 
import java.awt.Toolkit; 
 
/* Interface (una clase abstracta pura, sin metodos definidos) usado 
 * para escribir manejadores de escucha de eventos. */ 
import java.beans.PropertyChangeListener; 
 
/* Objeto lanzado cuando se produce un cambio en las propiedades de un 
 * objeto. */ 
import java.beans.PropertyChangeEvent; 
 
/* Un componente en Java es un objeto grafico que puede interactuar 
 * con el usuario. */ 
import java.awt.Component; 
 
/* El evento de ventana. */ 
import java.awt.event.WindowEvent; 
 
/* El evento de teclado. */ 
import java.awt.event.KeyEvent; 
 
/* Interface usado cuando deseamos escuchar los cambios de estado que 
 * se producen en los componentes. */ 
import java.awt.event.ComponentListener; 
 
/* El evento de cambio del estado de una componente. */ 
import java.awt.event.ComponentEvent; 
 
public class ModulatorControl 
    extends JPanel 
    implements Runnable, 
               WindowListener, 
               ChangeListener, 
               PropertyChangeListener, 
               ComponentListener { 
 
    /* Rango de frecuencias posible. */ 
    static final int MIN_FREQ = 0; 
    static final int MAX_FREQ = 22050; 
 
    /* Dimensiones iniciales de la ventana. */ 
    static final int WINDOW_WIDTH = 512; 
    static final int WINDOW_HEIGHT = 128; 
 
    JFormattedTextField textField; 
    JSlider slider; 
 
    /* La frecuencia leida del servidor. */ 
    int freq; 
 
    /* El canal TCP hacia el servidor. */ 
    OutToServer outToServer; 
 
    /* El canal multicast UDP desde el servidor. */ 
    InFromServer inFromServer; 
 
    /** 
     * Constructor. Crea la ventana con todos los componentes y 
     * establece los cockes de entrada y salida. 
     */ 
    public ModulatorControl(String serverName, 
                            int serverPort, 
                            String multicastAddr, 
                            int multicastPort) 
        throws IOException { 
 
        /* Colocaremos el textField y el slider uno encima del otro. */ 
        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); 
 
        /* Creamos el label del textField. */ 
        JLabel textFieldLabel = new JLabel("Frecuencia(Hz):", JLabel.CENTER); 
        textFieldLabel.setAlignmentX(Component.CENTER_ALIGNMENT); 
 
        /* Creamos el textField con su formato. */ 
        java.text.NumberFormat numberFormat 
            = java.text.NumberFormat.getIntegerInstance(); 
        NumberFormatter formatter = new NumberFormatter(numberFormat); 
        formatter.setMinimum(new Double((double)MIN_FREQ)); 
        formatter.setMaximum(new Double((double)MAX_FREQ)); 
        textField = new JFormattedTextField(formatter); 
        textField.setValue(new Integer(MIN_FREQ)); 
        textField.setColumns(4); 
        textField.addPropertyChangeListener(this); 
 
        /* El textField comprobara el valor introducido por teclado 
         * una vez que hallamos pulsado la techa <Enter>. */ 
        textField.getInputMap(). 
            put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),"check"); 
        textField.getActionMap().put("check", new AbstractAction() { 
            public void actionPerformed(ActionEvent e) { 
                if (!textField.isEditValid()) { // Texto invalido. 
                    Toolkit.getDefaultToolkit().beep(); 
                    textField.selectAll(); 
                } else try {                    // Texto valido, 
                    textField.commitEdit();     // usalo. 
                } catch (java.text.ParseException exc) { } 
            } 
        }); 
 
        /* Creamos el slider y definimos su apariencia. */ 
        slider = new JSlider(JSlider.HORIZONTAL, MIN_FREQ, MAX_FREQ, MIN_FREQ); 
        slider.addChangeListener(this); 
        slider.setMajorTickSpacing(1000); 
        slider.setMinorTickSpacing(1000); 
        slider.setPaintTicks(true); 
        slider.setPaintLabels(true); 
        //slider.setSnapToTicks(true); 
        slider.setBorder(BorderFactory.createEmptyBorder(0,0,10,0)); 
 
        /* Redefinimos las etiquetas del slider. */ 
        java.util.Dictionary labelTable = slider.getLabelTable(); 
        JLabel[] l = new JLabel[(MAX_FREQ-MIN_FREQ)/1000+1]; { 
            int i; 
            for(i=MIN_FREQ; i<MAX_FREQ; i+= 1000) { 
                l[i/1000] = new JLabel(i/1000 + ""); 
                labelTable.put(new Integer(i), l[i/1000]); 
            } 
        } 
        slider.setLabelTable(labelTable); 
        /*java.util.Hashtable labelTable = new java.util.Hashtable(); 
        JLabel[] l = new JLabel[(MAX_FREQ-MIN_FREQ)/1000+1]; { 
            int i; 
            for(i=MIN_FREQ; i<MAX_FREQ; i+= 1000) { 
                l[i/1000] = new JLabel(i/1000 + ""); 
                labelTable.put(new Integer(i), l[i/1000]); 
            } 
        } 
        slider.setLabelTable(labelTable);*/ 
        //slider.setPaintLabels(true); 
 
        /* Creamos una estructura subpanel para el textField y su 
         * etiqueta. */ 
        JPanel labelAndTextField = new JPanel(); 
        labelAndTextField.add(textFieldLabel); 
        labelAndTextField.add(textField); 
 
        /* Insertamos en la ventana "ModulatorControl" el anterior JPanel y 
         * el slider. */ 
        add(labelAndTextField); 
        add(slider); 
 
        /* Distancia del contenido de la ventana a sus bordes. */ 
        setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); 
 
        /* Queremos una ventana "bonita" con la decoracion y 
         * comportamiento por defecto en Java. */ 
        JFrame.setDefaultLookAndFeelDecorated(true); 
 
        /* Creamos la ventana y establecemos su tama~no. */ 
        JFrame frame = new JFrame("ModulatorControl"); 
 
        /* Le indicamos el tama~no. */ 
        frame.setPreferredSize(new java.awt.Dimension(WINDOW_WIDTH, WINDOW_HEIGHT)); 
        /* Espeficicamos lo que hace cuando lo cerramos: finalizar. */ 
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
 
        /* Insertamos el "pane" en la ventana actual. Un pane es la 
         * estructura de objetos creada cuando hemos ido a~nadiendo los 
         * diferentes objetos usando el metodo "add()". */ 
        //this.setOpaque(true); //content panes must be opaque 
        frame.setContentPane(this); 
 
        /* Hace que la ventana tenga el tama~no especificado por el 
         * metodo "setPreferredSize()". */ 
        frame.pack(); 
 
        /* Mostramos la ventana. */ 
        frame.setVisible(true); 
 
        /* Nos conectamos al servidor */ 
        outToServer = new OutToServer(serverName, serverPort); 
        inFromServer = new InFromServer(multicastAddr, multicastPort); 
 
        /* Hacemos que la ventana escuche a los eventos de ventana. */ 
        frame.addWindowListener(this); 
 
        /* Hacemos que la ventana escuche a los eventos provocamos por 
         * sus componentes. */ 
        frame.addComponentListener(this); 
 
        /* Creamos y lanzamos el hilo que esta pendiente de las 
         * frecuencias especificadas por los demas clientes. */ 
        new Thread(this).start(); 
   } 
 
    /** 
     * Este metodo es el que ejecuta el hilo que escucha las 
     * frecuencias de los demas clientes. Basicamente es un lazo sin 
     * fin que espera a recibir un paquete del canal multicast. Cuando 
     * lo recibe extrae de el la frecuencia y actualiza la variable 
     * global del metodo "freq". 
     */ 
    public void run() { 
        for(;;) { 
            try { 
                //freq = inFromServer.receive(); 
                byte[] data = inFromServer.receive(); 
                freq = ((int)data[0] & 0xFF) * 256 + ((int)data[1] & 0xFF); 
            } catch (IOException e) { 
                System.out.println("ModulatorControl:run:unabletoreceivedata"); 
            } 
            //System.out.println("freq = " + freq); 
 
            /* Cuando recibimos una frecuencia actualizamos el campo 
             * textField. */ 
            textField.setValue(new Integer(freq)); 
        } 
    } 
 
    /** 
     * Metodo que se invoca cuando la ventana se abre. 
     */ 
    public void windowOpened(WindowEvent e) {} 
 
    /** 
     * Metodo que se invoca cuando la ventana se iconiza. 
     */ 
    public void windowIconified(WindowEvent e) {} 
 
    /** 
     * Metodo que se invoca cuando la ventana se des-iconiza. 
     */ 
    public void windowDeiconified(WindowEvent e) {} 
 
    /** 
     * Metodo que se invoca cuando la ventana se activa (cuando se 
     * selecciona). 
     */ 
    public void windowActivated(WindowEvent e) {} 
 
    /** 
     * Metodo que se invoca cuando la ventana se des-activa. 
     */ 
    public void windowDeactivated(WindowEvent e) {} 
 
    /** 
     * Metodo que se invoca cuando la ventana se va cerrar. 
     */ 
    public void windowClosing(WindowEvent e) { 
        try { 
            /* Enviamos una frecuencia negativa indicando al servidor 
             * que desemoa cerrar la conexion TCP. */ 
            outToServer.sendString("-1\n"); 
        } catch (IOException ioe) { 
            System.out.println("ModulatorControl::windowClosing:unabletosenddata"); 
        } 
        try { 
            /* Abandonamos el grupo multicast. */ 
            inFromServer.leaveGroup(); 
        } catch (IOException ioe) { 
            System.out.println("ModulatorControl::windowClosing:unabletoleavethemulticastgroup"); 
        } 
        /* Cerramos el socket de recepcion (canal multicast). */ 
        inFromServer.close(); 
    } 
 
    /** 
     * Metodo que se invoca cuando la ventana ya se ha cerrado. 
     */ 
    public void windowClosed(WindowEvent e) {} 
 
    /** 
     * Metodo que se invoca cuando el slider cambia de estado. 
     */ 
    public void stateChanged(ChangeEvent e) { 
        JSlider source = (JSlider)e.getSource(); 
        int local_freq = (int)source.getValue(); 
        if (!source.getValueIsAdjusting()) { // done adjusting 
            textField.setValue(new Integer(local_freq)); 
        } else { //value is adjusting; just set the text 
            textField.setText(String.valueOf(local_freq)); 
        } 
        /* Si la frecuencia leida a traves del canal multicast es 
         * diferente de la que indica el slider, es que estamos 
         * interactuando con el slider. Por tanto enviamos la que 
         * espeficica este hacia al servidor. */ 
        if(freq!=local_freq) { 
            try { 
                //System.out.println(freq + " " + local_freq); 
                outToServer.sendString(String.valueOf(local_freq) + \n); 
            } catch (IOException ioe) { 
                System.out.println("ModulatorControl::stateChanged:unabletosenddata"); 
            } 
        } 
    } 
 
    /** 
     * Escucha al textField. Este metodo detecta cuando el valor del 
     * texField cambia (por el motivo que sea). 
     */ 
    public void propertyChange(PropertyChangeEvent e) { 
        if ("value".equals(e.getPropertyName())) { 
            Number value = (Number)e.getNewValue(); 
            if (slider != null && value != null) { 
                slider.setValue(value.intValue()); 
            } 
        } 
    } 
 
    /** 
     * Metodo invocado si los componentes de la ventana son escondidos. 
     */ 
    public void componentHidden(ComponentEvent e) {} 
 
    /** 
     * Metodo que se invoca si los componentes de la ventana son 
     * movidos. 
     */ 
    public void componentMoved(ComponentEvent e) {} 
 
    /** 
     * Metodo invocado cuando los componentes cambian de tama~no. 
     */ 
    public void componentResized(ComponentEvent e) {} 
 
    /** 
     * Metodo invocado cuando los componentes son mostrados. 
     */ 
    public void componentShown(ComponentEvent e) {} 
 
    /** 
     * El metodo main() es el primero que se ejecuta cuando se lanza 
     * un objeto de la clase ModulatorControl. Su funcion principal es la 
     * de tratar los argumentos proporcionados desde el shell a traves 
     * de la linea de comandos y de crear el objeto en cuestion. 
     */ 
    public static void main(String args[]) throws Exception { 
        if(args.length <1) { 
            System.out.print("Uso:javaModulatorControlserverserverPort"); 
            System.out.println("multicastAddrmulticastPort"); 
        } else { 
            String server = args[0]; 
            int serverPort = Integer.parseInt(args[1]); 
            String multicastAddr = args[2]; 
            int multicastPort = Integer.parseInt(args[3]); 
            System.out.println("ModulatorControl.class:server=" + server); 
            System.out.println("ModulatorControl.class:serverPort=" + 
                               serverPort); 
            System.out.println("ModulatorControl.class:multicastaddress=" + 
                               multicastAddr); 
            System.out.println("ModulatorControl.class:multicastport=" + 
                               multicastPort); 
            new ModulatorControl(server, serverPort, multicastAddr, 
                                 multicastPort); 
        } 
    } 
}

11 El cdigo de dc.c

 
/* 
 * dc.c -- Un generador de seal constante (Direct Current). 
 * 
 * gse. 2005. 
 * 
 */ 
 
/****************************************************************************** 
 * 
 * Ficheros cabecera. 
 * 
 *****************************************************************************/ 
 
/* Entrada y salida de streams buffereados. */ 
#include <stdio.h> 
 
/* Biblioteca de funciones estndar (exit(), EXIT_SUCCESS, 
   EXIT_FAILURE, ...) */ 
#include <stdlib.h> 
 
/* Manipulacin de cadenas y movimiento de memoria (memset(), ...). */ 
#include <string.h> 
 
/* Biblioteca estndar de funciones relacionadas con el sistema 
   operativo Unix (read(), write(), getopt(), ...). */ 
#include <unistd.h> 
 
/* Signals (interrupciones) (signal(), SIGINT). */ 
#include <signal.h> 
 
/* POSIX Threads (pthread_t, pthread_create(), ...). */ 
#include <pthread.h> 
 
/* Sockets. */ 
/* Ms info en: 
 * 
 * http://www.csce.uark.edu/~aapon/courses/os/examples/concurrentserver.c 
 * http://beej.us/guide/bgnet/ 
 */ 
 
/* Tipos de datos primitivos del sistema. */ 
#include <sys/types.h> 
 
/* Sockets (socket(), listen(), ...). */ 
#include <sys/socket.h> 
 
/* Direcciones IP, opciones y definiciones. */ 
#include <netinet/in.h> 
 
/* Servicio de resolucin de nombres. */ 
#include <netdb.h> 
 
/* Signals (interrupciones) (signal(), SIGINT). */ 
#include <signal.h>  /* signal(), SIGINT */ 
 
#include <getopt.h> 
 
/****************************************************************************** 
 * 
 * Definiciones. 
 * 
 *****************************************************************************/ 
 
/* Tamao del buffer de datos. */ 
#define BUFFER_SIZE 512 
 
/* Valor por defecto de la seal. */ 
#define DEFAULT_DC 1000 
 
/* Puerto de comunicacin con los controladores de parmetros. */ 
#define MULTICAST_PORT 5678 
 
/* Puerto usado para atender a los clientes. */ 
#define LISTENING_PORT 5678 
 
/* Direccin multicast por defecto. Todos los clientes escuchan dicha 
   direccin multicast. */ 
#define MULTICAST_ADDR (char *)"224.0.0.1" 
 
/* Nmero mximo de clientes que esperan a que la conexin sea 
   establecida. */ 
#define QLEN 1 
 
/****************************************************************************** 
 * 
 * Variables Globales. 
 * 
 *****************************************************************************/ 
 
/****************************************************************************** 
 * 
 * Funciones. 
 * 
 *****************************************************************************/ 
 
/* Cuerpo principal del programa. */ 
int main(int argc, char *argv[]) { 
  work(argc, argv); 
  return EXIT_SUCCESS; 
} 
 
work(int argc, char *argv[]) { 
 
  /* Si pulsamos CRTL+C, el programa acaba ejecutando la funcin 
     end(). */ 
  void end() { 
    fprintf(stderr,"%s:CTRL+Cdetected.Exiting...",argv[0]); 
    fprintf(stderr,"done\n"); 
    exit(EXIT_SUCCESS); 
  } 
  signal(SIGINT, end);  //activando la senal SIGINT 
 
  /* Valor de la seal DC. */ 
  int dc_val = DEFAULT_DC; 
 
  /* Puerto de escucha de los clientes. */ 
  short listening_port = LISTENING_PORT; 
 
  /* Puerto destino de los datagramas multicast. */ 
  short multicast_port = MULTICAST_PORT; 
 
  /* Canal multicast destino. */ 
  char *multicast_addr = MULTICAST_ADDR; 
 
  /* Manipulacin de los parmetros de entrada. */ { 
    int c; 
    while(1) { 
 
      static struct option long_options[] = { 
        {"dc_val", required_argument, 0, d}, 
        {"listening_port", required_argument, 0, l}, 
        {"multicast_addr", required_argument, 0, a}, 
        {"multicast_port", required_argument, 0, m}, 
        {"help", no_argument, 0, ?}, 
        {0, 0, 0, 0} 
      }; 
 
      int option_index = 0; 
 
      c = getopt_long(argc, argv, "d:l:a:m:?", long_options, &option_index); 
      if(c==-1) { 
        /* No more options. */ 
        break; 
      } 
 
      switch (c) { 
      case 0: 
        /* If this option set a flag, do nothing else now. */ 
        if (long_options[option_index].flag != 0) 
          break; 
        fprintf(stderr, "option%s", long_options[option_index].name); 
        if (optarg) 
          fprintf(stderr, "witharg%s", optarg); 
        fprintf(stderr, "\n"); 
        break; 
 
      case d: 
        dc_val= atoi(optarg); 
        break; 
 
      case l: 
        listening_port = atoi(optarg); 
        break; 
 
      case a: 
        multicast_addr = optarg; 
        break; 
 
      case m: 
        multicast_port = atoi(optarg); 
        break; 
 
      case ?: 
        fprintf(stdout, 
                "-[-a]mplitude=frequency(inHz)ofthemodulatingsignal(%d)\n", 
                dc_val); 
        fprintf(stdout, 
                "-[-J]PG=porttoservetheModulatorControlclients(%d)\n", 
                listening_port); 
        fprintf(stdout, 
                "-[-]multicast_[a]ddress=multicastaddresstocomunicate"); 
        fprintf(stdout, 
                "totheclientsachangeinthevalueofthefrequency(%s)\n", 
                multicast_addr); 
        fprintf(stdout, 
                "-[-]multicast_[p]ort=porttowhichthemulticast"); 
        fprintf(stdout, 
                "packetsaresent(%d)\n", 
                multicast_port); 
        exit(1); 
 
      default: 
        fprintf(stderr, "%s:Unrecognizedargument.Aborting...\n", argv[0]); 
        abort(); 
      } 
    } 
  } 
 
  fprintf(stderr, "%s:DCvalue=%dHz\n" ,argv[0], dc_val); 
  fprintf(stderr, "%s:listeningport=%d\n", argv[0], listening_port); 
  fprintf(stderr, "%s:multicastaddress=%s\n", argv[0], multicast_addr); 
  fprintf(stderr, "%s:multicastport=%d\n", argv[0], multicast_port); 
 
 
  /* Un semforo para acceder "en exclusin mutua" a una zona de 
     cdigo. */ 
  pthread_mutex_t mut; 
 
  /* Creamos el semforo. */ 
  pthread_mutex_init(&mut, NULL); 
 
  /* Hilo que genera la seal constante. */ 
  void *generator(void *arg) { 
 
    for(;;) { 
      short buffer[BUFFER_SIZE/sizeof(short)]; 
      int i; 
      for(i=0; i<BUFFER_SIZE/sizeof(short); i++) { 
        buffer[i] = dc_val; 
      } 
      write(1, (void *)buffer, BUFFER_SIZE); 
    } 
 
    /* Finalizamos el thread. */ 
    pthread_exit(0); 
 
  } 
 
    /* Lanzamos el hilo anterior. */ 
  pthread_t generator_tid; 
  if(pthread_create(&generator_tid, NULL, generator, NULL) == -1) { 
    perror("pthread_create"); 
    exit(EXIT_FAILURE); 
  } 
 
  /* Creamos el socket TCP de escucha. */ 
  int listen_sd; { 
 
    /* Obtenemos el nmero del protocolo. */ 
    struct protoent *ptrp; /* Pointer to a protocol table entry. */ 
    ptrp = getprotobyname("tcp"); 
    if((long)(ptrp) == 0) { 
      perror("getprotobyname"); 
      exit(EXIT_FAILURE); 
    } 
 
    /* Creamos el socket. */ 
    listen_sd = socket (PF_INET, SOCK_STREAM, ptrp->p_proto); 
    if(listen_sd < 0) { 
      perror("socket"); 
      exit(EXIT_FAILURE); 
    } 
 
    /* Usaremos el puerto de servicio sin esperar a que ste est 
       libre. */ 
    int yes = 1; 
    if(setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) { 
      perror("setsockopt"); 
      exit(EXIT_FAILURE); 
    } 
  } 
 
  /* Asignamos una direccin (dir IP, puerto) al socket de escucha. */ { 
    struct sockaddr_in sad;              /* Direccin del servidor. */ 
    memset((char  *)&sad,0,sizeof(sad)); /* Borramos la estructura. */ 
    sad.sin_family = AF_INET;            /* Usaremos Internet. */ 
    sad.sin_addr.s_addr = INADDR_ANY;    /* Cualquiera de las IP 
                                            asignadas al host vale. */ 
    sad.sin_port = 
      htons((u_short)listening_port);    /* Asignamos el puerto de escucha. */ 
    if (bind(listen_sd, (struct sockaddr *)&sad, sizeof (sad)) < 0) { 
      perror("bind"); 
      exit(EXIT_FAILURE); 
    } 
  } 
 
  /* Comenzamos a escuchar. */ 
  if (listen(listen_sd, QLEN) < 0) { 
    perror("listen"); 
    exit(EXIT_FAILURE); 
  } 
 
  /* Creamos el socket UDP. */ 
  int info_sd; 
  if((info_sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 
    perror("socket"); 
    exit(EXIT_FAILURE); 
  } 
 
  /* Dir IP y puerto del socket. */ 
  struct sockaddr_in info_addr; { 
    /* Usaremos Internet. */ 
    info_addr.sin_family = AF_INET; 
    /* IP multicast destino de los datagramas. */ 
    info_addr.sin_addr.s_addr = inet_addr(multicast_addr); 
    /* Puerto destino de los datagramas. */ 
    info_addr.sin_port = htons(multicast_port); 
    memset(&(info_addr.sin_zero), \0, 8); 
  } 
 
  fprintf(stderr,"%s:DCvalue=\"%d\"\n",  argv[0],dc_val); 
 
  /* Lazo del servidor. */ 
  while(1) { 
 
    fprintf(stderr,"dc:waitingforconnection...\n"); 
 
    /* Socket para "servir" a los clientes. */ 
    long serve_sd; 
 
    /* Esperamos a que un cliente se conecte. */ { 
      struct sockaddr_in cad;        /* Clients address. */ 
      socklen_t alen = sizeof(cad);  /* Tamao de la direccin. */ 
      serve_sd = accept(listen_sd, (struct sockaddr *)&cad, &alen); 
      if(serve_sd<0) { 
        perror("accept"); 
        exit (EXIT_FAILURE); 
      } 
    } 
 
    /* Hilo que controla a un cliente. */ 
    void *service(void *arg) { 
 
      /* El socket de conmunicacin con el cliente. Ntese que cada 
         cliente posee una variable "sd" diferente. */ 
      long sd = (long)arg; 
 
      /* Mientras la amplitud recibida sea >= 0. OJO */ 
      for(;;) { 
        int new_dc_val; 
        receive_from_client(sd, &new_dc_val); 
        if(new_dc_val<0) break; 
 
        /* Seccin crtica. */ 
        pthread_mutex_lock(&mut); 
        dc_val = new_dc_val; 
        fprintf(stderr,"%s:DCvalue=\"%d\"\n", argv[0], dc_val); 
        pthread_mutex_unlock(&mut); 
        /* Fin de la seccin crtica. */ 
 
        inform_to_client(info_sd, info_addr, dc_val); 
      } 
 
      /* Cerramos la conexin con el cliente. */ 
      close(sd); 
 
      /* Finalizamos el thread. */ 
      pthread_exit(0); 
    } 
 
    /* Lanzamos el hilo. */ 
    pthread_t service_tid; /* Thread identificator. */ 
    if(pthread_create(&service_tid, NULL, service, (void *)serve_sd) == -1) { 
      perror("pthread_create"); 
      exit(EXIT_FAILURE); 
    } 
 
  } 
 
  /* Cerramos el socket de escucha (cuando pulsamos CTRL+C). */ 
  close(listen_sd); 
} 
 
/* Enva un paquete UDP con la amplitud de la seal. */ 
inform_to_client(int sd, struct sockaddr_in addr, int amplitude) { 
  unsigned char message[2]; 
  message[0] = amplitude/256; 
  message[1] = amplitude%256; 
  sendto(sd, message, 2, 0,(struct sockaddr *) &addr, sizeof(addr)); 
} 
 
/* Recibe del cliente la nueva frecuencia. */ 
receive_from_client(int sd, int *dc_val) { 
  char message[256]; 
  read_text_line(sd, message); 
  sscanf(message,"%d",dc_val); 
} 
 
/* Lee una lnea de texto ASCII. */ 
read_text_line(int sd, char *str) { 
  int n; 
  do { 
    n = recv(sd, str, 1, 0); 
  } while (n>0 && *str++ != \n); 
} 
 
#ifdef _1_ 
  int c; 
  while ((c = getopt (argc, argv, "v:")) != -1) { 
    switch (c) { 
    case v: 
      dc_val = atoi(optarg); 
      break; 
    case ?: 
      if (isprint (optopt)) 
        fprintf (stderr, "Unknownoption‘-%c’.\n", optopt); 
      else 
        fprintf (stderr, 
                 "Unknownoptioncharacter‘\\x%x’.\n", 
                 optopt); 
      return 1; 
    default: 
      abort (); 
    } 
  } 
 
  fprintf(stderr,"%s:DCvalue=\"%d\"\n", 
          argv[0],dc_val); 
 
 
 
} 
#endif

12 Sobre la compilacin

En esta prctica se usan tres programas distintos (dc, modulator y ModulatorControl) y cada uno de ellos tiene un Makefile distinto. A continuacin se muestran dichos ficheros:

12.1 Compilacin de dc

 
%.class: %.java 
        javac -classpath ../Server $*.java 
 
%: %.c 
        gcc $< -o $@ -lpthread 
 
%: %.py 
        cp $*.py $@ 
 
EXE =  dc \ 
        DcControl.class \ 
        DcControl \ 
        ../Server/InFromServer.class \ 
        ../Server/OutToServer.class 
 
all:  $(EXE) 
 
clean: 
        rm -f $(EXE) 
 
install: 
        cp $(EXE) *.class ~/bin 
 
publish: 
        rm -rf /tmp/dc 
        svn export . /tmp/dc 
        tar --create --file=/tmp/dc.tar -C /tmp dc 
        gzip -9 /tmp/dc.tar 
        scp /tmp/dc.tar.gz www.ace.ual.es:~/public_html/imyso 
        rm /tmp/dc.tar.gz

12.2 Compilacin de modulator and ModulatorControl

 
%.class: %.java 
        javac -classpath ../Server $*.java 
 
%: %.c 
        gcc $< -o $@ -lm -lpthread 
 
%: %.py 
        cp $*.py $@ 
 
EXE =  modulator \ 
        ModulatorControl.class \ 
        ModulatorControl \ 
        ../Server/InFromServer.class \ 
        ../Server/OutToServer.class 
 
all:  $(EXE) 
 
clean: 
        rm -f $(EXE) 
 
install: 
        cp $(EXE) *.class ~/bin 
 
publish: 
        rm -rf /tmp/modulator 
        svn export . /tmp/modulator 
        tar --create --file=/tmp/modulator.tar -C /tmp modulator 
        gzip -9 /tmp/modulator.tar 
        scp /tmp/modulator.tar.gz www.ace.ual.es:~/public_html/imyso 
        rm /tmp/modulator.tar.gz

13 Ejemplos de utilizacin

Para ejecutar correctamente esta aplicacion debemos lanzar modulator en el host que posee la tarjeta de sonido. El/los cliente/s que controla/n la frecuencia de la portandora puede/n ejecutarse tambin es ese host o en cualquier otro host de Internet (siempre que el trfico multicast est permitido).

Como se puede ver en el cdigo de modulator.c, el socket que hemos creado para controlar la frecuencia acepta una cadena de caracteres que representan un nmero entero. Puesto que dicha cadena est en formato ASCII, podemos usar el programa estndar telnet para controlar la frecuencia de la portadora. Para hacer esto escribiremos:

telnet host_que_ejecuta_modulator PORT

donde PORT es el puerto de escucha, definido en el fichero modulator.c. Tras establecer la conexin podremos cambiar la frecuencia tecleando una nueva (y pulsando la tecla Enter). La otra forma de controlar la frecuencia consiste en usar ModulatorControl. Para ejecutar este programa escribieremos:

java ModulatorControl host_que_ejecuta_modulator

en el directorio que contiene las clases compiladas (los ficheros .class).

Por supuesto, es necesita una mquina virtual de Java instalada en cada host cliente.

Bueno, estos son algunos ejemplos interesantes:

  1. Generando una sinusoide y controlando su frecuencia con telnet:
    localhost$ dc | modulator | aplay -f cd -t raw  
    localhost$ telnet localhost 6789

  2. Generando una sinusoide y controlando su frecuencia con ModulatorControl:
    localhost$ dc | modulator | aplay -f cd -t raw  
    localhost$ java ModulatorControl localhost

  3. Generando una sinusoide, controlando su frecuencia con ModulatorControl y visualizando el espectro:
    localhost$ dc | modulator | java RTASA  
    localhost$ java ModulatorControl localhost

  4. Modulando en la mquina local:
    localhost$ arecord -f cd -t raw | modulator | aplay -f cd -t raw  
    localhost$ java ModulatorControl localhost

  5. Modulando desde una mquina remota:
    host_remoto$ arecord -f cd -t raw | modulator | aplay -f cd -t raw  
    localhost$ java ModulatorControl host_remoto

  6. Modulando desde varias mquinas:
    host_remoto$ arecord -f cd -t raw | modulator | aplay -f cd -t raw  
    host_remoto$ java ModulatorControl localhost  
    localhost$ java ModulatorControl host_remoto