<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE document PUBLIC "-//CNX//DTD CNXML 0.5 plus MathML//EN" "http://cnx.rice.edu/technology/cnxml/schema/dtd/0.5/cnxml_mathml.dtd">
<document xmlns="http://cnx.rice.edu/cnxml" xmlns:md="http://cnx.rice.edu/mdml/0.4" xmlns:bib="http://bibtexml.sf.net/" xmlns:m="http://www.w3.org/1998/Math/MathML" id="new">
  <name>Segnali nel tempo: soglie e filtri</name>
  <metadata>
  <md:version>1.2</md:version>
  <md:created>2007/06/08 08:03:59 GMT-5</md:created>
  <md:revised>2008/03/20 06:11:37.749 GMT-5</md:revised>
  <md:authorlist>
      <md:author id="drocchesso">
      <md:firstname>Davide</md:firstname>
      
      <md:surname>Rocchesso</md:surname>
      <md:email>roc@iuav.it</md:email>
    </md:author>
  </md:authorlist>

  <md:maintainerlist>
    <md:maintainer id="drocchesso">
      <md:firstname>Davide</md:firstname>
      
      <md:surname>Rocchesso</md:surname>
      <md:email>roc@iuav.it</md:email>
    </md:maintainer>
  </md:maintainerlist>
  
  <md:keywordlist>
    <md:keyword>events</md:keyword>
    <md:keyword>filtering</md:keyword>
    <md:keyword>hysteresis</md:keyword>
    <md:keyword>Thresholds</md:keyword>
    <md:keyword>timers</md:keyword>
  </md:keywordlist>

  <md:abstract>Nell'elaborazione di segnali catturati da sensori c'è necessità di effettuare alcune operazioni ricorrenti, quali il filtraggio o la sogliatura.</md:abstract>
</metadata>
  <content>
    
    <section>
      <name>Isteresi e soglie</name>
      <para id="isteresip">
	L'<link src="http://en.wikipedia.org/wiki/Hysteresis">isteresi</link>
	è quella proprietà dei sistemi che li fa reagire con un certo
	ritardo alle forze ad essi applicate. Ad esempio, all'atto di
	sedersi su una poltrona imbottita, la relazione tra forza e
	compressione del cuscino può essere di <link src="http://upload.wikimedia.org/wikipedia/commons/2/2a/Hysteresiscurve.png">forma
	isteretica</link>, ed evidenza di ciò si può avere
	dall'impronta lasciata nel momento in cui ci si rialza.

	Nel design dell'interazione, l'isteresi viene introdotta
	deliberatamente per ritardare le azioni del sistema rispetto
	all'accadere di eventi. Per esempio, il click del mouse in una
	certa area può attivare un menu, la cui scomparsa viene però
	ritardata rispetto all'uscita da quella regione, in modo da
	consentire all'utente di esplorare le voci del menu anche con
	percorsi del mouse imperfetti.

	Nei regolatori automatici, quale è ad esempio il termostato
	  per la regolazione di temperatura, è spesso presente un
	  elemento isteretico per evitare commutazioni spurie, ad
	  esempio dovute ai rimbalzi degli interruttori. Il
	  termostato, in particolare, attiva il bruciatore quando la
	  temperatura scende al di sotto di una soglia
	  (<emphasis>threshold</emphasis>) <m:math> <m:ci>A</m:ci>
	  </m:math>, ma non lo spegne fintantoché la temperatura non
	  raggiunge una seconda soglia <m:math>
	  <m:apply>
	    <m:gt/>
	    <m:ci>B</m:ci>
	    <m:ci>A</m:ci>
	  </m:apply>
	</m:math>. Quindi, l'accensione e spegnimento del bruciatore
	dipendono dalla storia del segnale di temperatura. Ciò
	previene sequenze rapide di accensione/spegnimento per
	oscillazioni di temperatura intorno alla soglia.

	Nella sua <link src="http://upload.wikimedia.org/wikipedia/commons/a/aa/Hysteresis_sharp_curve.svg">forma
	più stilizzata</link>, un sistema con isteresi è
	bistabile. Cioè caratterizzato da due stati stabili. Quindi,
	per programmare un interruttore con isteresi è sufficiente
	utilizzare una variabile <code>boolean acceso</code> e due
	soglie <code>basso</code> e <code>alto</code>:
	<code type="block">
<![CDATA[
	  if (acceso) {
	     if (signal < basso) {
	        // operazioni di spegnimento
	        acceso = false;
	     }
	  }
	  else //spento
	     if (signal > alto) {
	        // operazioni di accensione
	        acceso = true;
	     }
]]>
	</code>
      </para>
      <exercise id="audioThreshold">
	<problem>
	  <para id="audioThresholdp">
	    Si scriva un programma Processing che disegni la curva
	    della pressione acustica catturata dal microfono. A basse
	    pressioni la curva sia disegnata in verde. Quando la
	    pressione acustica supera una soglia superiore la curva
	    deve diventare di colore rosso, e tornare ad essere verde
	    quando torna al di sotto di una soglia inferiore.
	  </para>
	</problem>
	<solution>
	  <para id="audioThresholds">
	    Una soluzione che fa uso della libreria audio <link src="http://code.compartmental.net/tools/minim">Minim</link>
	    è la seguente:
	    <code type="block">
<![CDATA[	      
import ddf.minim.*;
 
AudioInput in;
int WIDTH=400, HEIGHT=200;
int x;
int level, prevLevel;
color colorLine=color(0,255,0);
boolean acceso=false;
int basso=80;
int alto=120;
 
void setup()
{
  size(WIDTH, HEIGHT);
  Minim.start(this);
  // get a line-in from Minim: mono, 512 sample buffer
  // default sampling rate is 44100, default bit-depth is 16
  in = Minim.getLineIn(Minim.MONO, 512);
  
}
 
void draw()
{
  if (x==0) {
    background(0);
    stroke(255,0,0); line(0,HEIGHT-alto,WIDTH,HEIGHT-alto);
    stroke(0,255,0); line(0,HEIGHT-basso,WIDTH,HEIGHT-basso);
  }
  stroke(colorLine);
  level = int(in.left.level()*HEIGHT);
  line(x, HEIGHT - prevLevel, (x+1)%WIDTH, HEIGHT - level);
  x = (x+1)%WIDTH;
  prevLevel = level;
  if (acceso) {
	     if (level < basso) {
                colorLine = color(0, 255, 0);
	        acceso = false;
	     }
    }
  else //spento
            {
	     if (level > alto) {
	        colorLine = color(255, 0, 0);
	        acceso = true;
	     }
  }
}
 
void stop()
{
  // always stop Minim
  Minim.stop();
  super.stop();
}
]]>
	    </code>
	  </para>
	</solution>
      </exercise>
      <exercise id="noiseGate">
	<problem>
	  <para id="noiseGatep">
	    Si modifichi il codice di <cnxn target="audioThreshold"/>
	    in modo da ottenere la curva di livello di un <link src="http://www.doctorproaudio.com/doctor/temas/dynamics-processors-noisegates_en.shtml">noise
	    gate</link>. Quando il livello scende al di sotto della
	    soglia verde, si applica uno smorzamento che gradualmente
	    forza il segnale ad essere inudibile. Quando il livello
	    supera la soglia rossa, si applica una amplificazione che
	    riporta il segnale al livello originale.
	  </para>
	</problem>
	<solution>
	  <para id="noiseGates">
	    Nella soluzione seguente il segnale viene abbattuto,
	      quando scende al di sotto della soglia inferiore,
	      mediante moltiplicazioni successive per un coefficiente
	      <code>gDown</code>, il cui valore (sempre minore di uno)
	      viene impostato in base al tempo desiderato di
	      smorzamento ed al frame rate. Lo smorzamento si
	      considera esaurito quando il segnale di ingresso viene
	      ad essere moltiplicato per <m:math> <m:mn>0.01</m:mn>
	      </m:math>. Analogamente, al superamento della soglia
	      superiore, il moltiplicatore viene riportato a uno
	      mediante moltiplicazioni successive per il coefficiente
	      <code>gUp</code>, di valore sempre maggiore di uno.
	    <code type="block">
<![CDATA[

import ddf.minim.*;
 
AudioInput in;
int WIDTH=400, HEIGHT=200;
int x;
int level, prevLevel, levelG;
color colorLine=color(0,255,0);
boolean acceso=false;
int basso=30;
int alto=50;
float damp=1.0;
float gain = 1.0;

float decayTime = 10.0; // in seconds
float raiseTime = 2.0;
float gDown = pow(10,-2/frameRate/decayTime);
float gUp = pow(10,2/frameRate/raiseTime);

 
void setup()
{
  println("gDown = " + gDown + "gUp = " + gUp);
  size(WIDTH, HEIGHT);
  Minim.start(this);
  // get a line-in from Minim: mono, 512 sample buffer
  // default sampling rate is 44100, default bit-depth is 16
  in = Minim.getLineIn(Minim.MONO, 512);
  
}
 
void draw()
{
  if (x==0) {
    background(0);
    stroke(255,0,0); line(0,HEIGHT-alto,WIDTH,HEIGHT-alto);
    stroke(0,255,0); line(0,HEIGHT-basso,WIDTH,HEIGHT-basso);
  }
  stroke(colorLine);
  level = int(in.left.level()*HEIGHT);  
  gain = gain*damp;
  //println(gain);
  if (gain > 1.0) gain = 1.0;
  if (gain < 0.01) gain = 0.01;
  levelG = int(level*gain);
  line(x, HEIGHT - prevLevel, (x+1)%WIDTH, HEIGHT - levelG);
  x = (x+1)%WIDTH;
  prevLevel = levelG;
  if (acceso) {
	     if (level < basso) {
                colorLine = color(0, 255, 0);
                damp = gDown;
	        acceso = false;
	     }
    }
  else //spento
            {
	     if (level > alto) {
	        colorLine = color(255, 0, 0);
                damp = gUp;
	        acceso = true;
	     }
  }
}
 
void stop()
{
  // always stop Minim
  Minim.stop();
  super.stop();
}
]]>
	    </code>
	  </para>
	</solution>
      </exercise>

      <para id="videothresholding">
	Gli esempi di sogliatura, isteresi, e
	<emphasis>gating</emphasis> visti nel caso dell'elaborazione
	del segnale audio sono di utilità generale per qualsiasi
	flusso di segnale proveniente da un sensore e, in particolare,
	anche nell'elaborazione di segnale video.
      </para>
    </section>
<section>
      <name>Filtri</name> 
      <para id="filtrip">
	Nella maggior parte delle applicazioni che trattano flussi di
        segnale provenienti da sensori, c'è la necessità di addolcire
        (<emphasis>smoothing</emphasis>) il profilo dei segnali
        stessi, in modo da evitare picchi improvvisi e rumore a rapida
        variabilità. Ciò si può fare con i <cnxn document="m12827">filtri</cnxn>.

	Il più semplice filtro di smoothing è il <cnxn document="m12827" target="averagingp"> filtro di media </cnxn>, il quale
	restituisce ad ogni istante il valor medio tra due campioni
	temporalmente contigui del segnale di ingresso. Interpretando
	i segnali nel dominio delle frequenze, l'operazione di
	smoothing corrisponde ad un filtraggio passa-basso, cioè ad
	una attenuazione delle frequenze elevate. Infatti, le
	variazioni repentine corrispondono a componenti di frequenza
	elevata. 

	Le capacità di smoothing del filtro di media sono limitate, e
	per ottenere degli smoothing più decisi (maggiore attenuazione
	delle alte frequenze) bisogna fare medie pesate di un numero
	maggiore di campioni del segnale di ingresso. Un filtro molto
	utile allo scopo è il <cnxn document="m12827" target="convfir2">filtro del secondo ordine simmetrico</cnxn>.

	Qualunque sia l'ordine del filtro, la sua realizzazione come
	media pesata di campioni del segnale di ingresso può essere
	effettuata convenientemente appoggiandosi alla realizzazione a
	<cnxn document="m14532" target="buffercircp">buffer
	circolare</cnxn>. In questo caso, la lettura avverrà da un
	certo numero di celle contigue a quella puntata da
	<code>OUT</code>, queste letture saranno pesate per i
	coefficienti del filtro, e il puntatore <code>IN</code>
	coinciderà con il puntatore <code>OUT</code>.
      </para>

      <exercise id="filtthresh">
	<problem>
	  <para id="filtthreshp"> Si modifichi il codice di <cnxn target="audioThreshold"/> pre-filtrando il segnale di
	    livello con un filtro del secondo ordine simmetrico.
	  </para>
	</problem>
	<solution>
	  <para id="filtthreshs">
	    Nel codice qui proposto si possono sperimentare diversi
	      livelli di smoothing, variando i valori di
	      <code>a0</code> e <code>a1</code>. Più questi valori
	      sono vicini, maggiore è l'entità dello smoothing. Per
	      non alterare con il filtraggio l'ampiezza generale del
	      segnale è opportuno che il doppio di <code>a0</code>,
	      sommato ad <code>a1</code>, dia come risultato <m:math>
	      <m:cn>1</m:cn> </m:math>.
 
	    <code type="block"><![CDATA[
import ddf.minim.*;
 
AudioInput in;
int WIDTH=400, HEIGHT=200;
int x;
int level, prevLevel;
color colorLine=color(0,255,0);
boolean acceso=false;
int basso=80;
int alto=120;

float GAIN = 10;
int BUFLEN = 5;
float[] buffer = new float[BUFLEN];
int bufPoint=2, bufPoint1=1, bufPoint2=0; 
float a0 = 0.3, a1 = 0.4; //smoothing
//float a0 = 0, a1 = 1; // no smoothing
void setup()
{
  size(WIDTH, HEIGHT);
  Minim.start(this);
  // get a line-in from Minim: mono, 512 sample buffer
  // default sampling rate is 44100, default bit-depth is 16
  in = Minim.getLineIn(Minim.MONO, 512);
  
}
 
void draw()
{
  if (x==0) {
    background(0);
    stroke(255,0,0); line(0,HEIGHT-alto,WIDTH,HEIGHT-alto);
    stroke(0,255,0); line(0,HEIGHT-basso,WIDTH,HEIGHT-basso);
  }
  stroke(colorLine);
  buffer[bufPoint] = in.left.level();
  level = int((a0*buffer[bufPoint] + a1*buffer[bufPoint1] + a0*buffer[bufPoint2])*GAIN*HEIGHT);
  bufPoint = (bufPoint+1)%BUFLEN;   bufPoint1 = (bufPoint1+1)%BUFLEN;   bufPoint2 = (bufPoint2+1)%BUFLEN;
  line(x, HEIGHT - prevLevel, (x+1)%WIDTH, HEIGHT - level);
  x = (x+1)%WIDTH;
  prevLevel = level;
  if (acceso) {
	     if (level < basso) {
                colorLine = color(0, 255, 0);
	        acceso = false;
	     }
    }
  else //spento
            {
	     if (level > alto) {
	        colorLine = color(255, 0, 0);
	        acceso = true;
	     }
  }
}
 
void stop()
{
  // always stop Minim
  Minim.stop();
  super.stop();
}
]]>
</code>	  
</para>
	</solution>
      </exercise>

      <para id="filtthreshconc">
	    Come si può dedurre dalla sperimentazione proposta in
	    <cnxn target="filtthresh"/>, prendendo medie pesate di
	    pochi campioni di segnale l'effetto di smoothing non è
	    macroscopico. Per avere un filtraggio più radicale bisogna
	    usare filtri di ordine elevato (elaborazione di molti
	    campioni di segnale) ovvero ricorrere a filtri ricorsivi.
	  </para>

	  <para id="filtriricorsivi">
	    Un filtro è un oggetto che, preso un segnale di ingresso,
	produce un segnale di uscita. Quando la produzione di un
	campione del segnale di uscita si ottiene elaborando campioni
	precedenti del segnale di uscita stesso (oltre che del segnale
	di ingresso) si dice che il filtro è <cnxn document="m12827" target="recursivediffeq">ricorsivo</cnxn>. La ricorsività
	consente filtraggi radicali a complessità modeste. Ad esempio,
	il filtro del primo ordine rappresentato dall'equazione alle
	differenze <equation id="iirdiffeq">
<m:math type="block">
	    <m:apply>
	      <m:eq/>
	      <m:apply>
		<m:ci type="fn">y</m:ci>
		<m:ci>n</m:ci>
	      </m:apply>
	      <m:apply>
		<m:plus/>
		<m:apply>
		  <m:times/>
		  <m:ci>a</m:ci>
		  <m:apply>
		    <m:ci type="fn">y</m:ci>
		  <m:apply>
		    <m:minus/>
		    <m:ci>n</m:ci>
		    <m:mn>1</m:mn>
		  </m:apply>
		  </m:apply>
		</m:apply>
		<m:apply>
		  <m:times/>
		      <m:apply>
			<m:minus/>
			<m:mn>1</m:mn>
			<m:ci>a</m:ci>
		      </m:apply>
		  <m:apply>
		    <m:ci type="fn">x</m:ci>
		      <m:ci>n</m:ci>
		  </m:apply>
		</m:apply>
	      </m:apply>
	    </m:apply>
	  </m:math> </equation> è molto efficace come filtro di
	smoothing. E' importante mantenere la stabilità del filtro,
	cioè evitare che un segnale di ingresso limitato produca
	un'uscita illimitata. Nel caso del filtro ricorsivo del primo
	ordine, ciò è garantito se <m:math>
	  <m:apply>
	    <m:lt/>
	    <m:apply>
	      <m:abs/>
	      <m:ci>a</m:ci>
	    </m:apply>
	    <m:mn>1</m:mn>
	  </m:apply>
	</m:math>. Lo smoothing del segnale è giocoforza accompagnato
	  da una certa latenza. Un segnale a gradino presentato in
	  ingresso provoca in uscita un segnale che raggiunge
	  asintoticamente il livello del gradino. Nel caso del filtro
	  ricorsivo del primo ordine, la transizione è tanto più lenta
	  quanto più <m:math> <m:ci>a</m:ci> </m:math> è prossimo a
	  <m:math> <m:ci>1</m:ci> </m:math>. Per la precisione, il
	  numero di campioni necessari per raggiungere il 99 per cento
	  del valore finale è pari a <m:math>
	    <m:apply>
	      <m:eq/>
	      <m:ci>n</m:ci>
	  <m:apply>
	    <m:divide/>
	    <m:mn>-2</m:mn>
	    <m:apply>
	      <m:log/>
	      <m:logbase>
		<m:mn>10</m:mn>
	      </m:logbase>
	      <m:ci>a</m:ci>
	    </m:apply>
	  </m:apply>
	    </m:apply>
	</m:math>. Ad esempio, con <m:math>
	  <m:apply>
	    <m:eq/>
	    <m:ci>a</m:ci>
	    <m:mn>0.9</m:mn>
	  </m:apply>
	</m:math> la transizione dura circa 44 campioni. Naturalmente, la relazione
	<m:math>
	  <m:apply>
	    <m:eq/>
	    <m:ci>a</m:ci>
	    <m:apply>
	      <m:power/>
	      <m:ci>10</m:ci>
	      <m:apply>
		<m:divide/>
		<m:ci>-2</m:ci>
		<m:mn>n</m:mn>
	      </m:apply>
	    </m:apply>
	  </m:apply>
	</m:math> consente di ricavare il valore del coefficiente che consente una transizione in <m:math>
	  <m:mn>n</m:mn>
	</m:math> campioni. Tale formula è stata usata anche per impostare i tempi di apertura e chiusura del
	<cnxn target="noiseGate">noise gate</cnxn>.
	  </para>

      <exercise id="filtrecthresh">
	<problem>
	  <para id="filtrecthreshp"> Si modifichi il codice di <cnxn target="audioThreshold"/> pre-filtrando il segnale di
	    livello con un filtro ricorsivo del primo ordine.
	  </para>
	</problem>
	<solution>
	  <para id="filtrecthreshs">

	    Si provino diversi valori di <code>a</code> nel codice qui proposto, mantenendo il vincolo di stabilità.

	    <code type="block">
<![CDATA[
import ddf.minim.*;
 
AudioInput in;
int WIDTH=400, HEIGHT=200;
int x;
int level, prevLevel;
color colorLine=color(0,255,0);
boolean acceso=false;
int basso=80;
int alto=120;

float GAIN = 10;
float currentLev;
float prevLev = 0;
float a = 0.95; // the closer to 1, the smoother the signal

void setup()
{
  size(WIDTH, HEIGHT);
  Minim.start(this);
  // get a line-in from Minim: mono, 512 sample buffer
  // default sampling rate is 44100, default bit-depth is 16
  in = Minim.getLineIn(Minim.MONO, 512);
  
}
 
void draw()
{
  if (x==0) {
    background(0);
    stroke(255,0,0); line(0,HEIGHT-alto,WIDTH,HEIGHT-alto);
    stroke(0,255,0); line(0,HEIGHT-basso,WIDTH,HEIGHT-basso);
  }
  stroke(colorLine);
  currentLev = a*prevLev + (1 - a)*in.left.level(); 
  prevLev = currentLev;
  level = int(currentLev*GAIN*HEIGHT);  
  line(x, HEIGHT - prevLevel, (x+1)%WIDTH, HEIGHT - level);
  x = (x+1)%WIDTH;
  prevLevel = level;
  if (acceso) {
	     if (level < basso) {
                colorLine = color(0, 255, 0);
	        acceso = false;
	     }
    }
  else //spento
            {
	     if (level > alto) {
	        colorLine = color(255, 0, 0);
	        acceso = true;
	     }
  }
}
 
void stop()
{
  // always stop Minim
  Minim.stop();
  super.stop();
}
]]>
	  </code>
	  </para>
	</solution>
      </exercise>

      <para id="filtroris">
	Particolare importanza rivestono i <cnxn document="m12827" target="iir2">filtri ricorsivi del secondo ordine</cnxn>, i
	quali fungono da modello per la realizzazioni di
	risonanze. Una risonanza elementare si manifesta come
	oscillazione smorzata. Se i coefficienti del filtro del
	secondo ordine sono opportunamente scelti in modo da produrre
	una risposta ai limiti della stabilità, quello che si ottiene
	è una realizzazione dell'oscillatore, alternativa a quelle
	proposte in <cnxn document="m14532"/>.
      </para>

    </section>
   </content>
  
</document>
