FFT de audio con Python

Una manera sencilla de realizar FFT desde cualquier sistema operativo sin necesidad de disponer de programas como MATLAB, Octave o Mathematica, es utilizando un script de Python. Este lenguaje cuenta con la ventaja de ser muy sencillo y además de disponer una ingente cantidad de librerías para poder hacer casi cualquier cosa.
El objetivo de este pequeño programa es realizar una FFT de un trozo de audio grabado directamente desde un micrófono. Para ello se realiza una escucha pasiva a la espera de un sonido cuyo volumen esté por encima de un determinado umbral. Este umbral se puede modificar con la variable threshold, y dependiendo del micrófono y su sensibilidad debe de ser ajustado de manera diferente. Una vez detectado, se graba unos pocos segundos de audio, cuya longitud puede ser modificada en el segundo bucle for. Actualmente graba aproximadamente 1 segundo, pero si se cambia el 7 por un 100 la longitud es de 13 segundos.
El audio se guarda como un archivo para ser posteriormente utilizado en el cálculo de la FFT y ser graficada al momento. Todo este procedimiento está dentro de un bucle infinito, por lo que, aunque la ventana de la gráfica es bloqueante, está continuamente escuchando.

Las librerías necesarias para poder ejecutar este script son: numpy, matplotlib, pyaudio y scipy.

Añadir ruta al $PATH

¿Cuantas veces hemos hecho ejecutado el siguiente comando desde el terminal de Linux?

Lo que hace el sistema operativo es buscar un ejecutable llamado “ls” y lo abre. Las carpetas donde busca son las que componen la variable $PATH. Si queremos añadir una carpeta distinta que las que hay por defecto, lo que podemos hacer es:

Una vez gedit se haya abierto, si queremos añadir una única carpeta, simplemente pegamos la siguiente línea al final del archivo:

Para poder añadir más de una carpeta hay que escribirla a continuación de la anterior separándola con dos puntos de la siguiente manera:

Una vez guardado, cerramos gedit, cerramos el terminal y volvemos a abrir otro.
Ahora al ejecutar

aparecerá la carpeta que hemos acabado de añadir, con lo que podremos ejecutar cualquier archivo ejecutable que haya dentro sin tener que especificar la ruta.

Decodificación MAP: algoritmo BCJR

La decodificación MAP es aquella en la que se decide qué símbolo ha sido transmitido en función de la probabilidad a posteriori, es decir \(P\left(x_i | y_j \right)\) o la probabilidad condicional de que si se ha recibido \(y_j\), se haya transmitido \(x_i\). Dicho de otro modo, ¿cuál es el símbolo más probable que la fuente haya podido enviar \(x_i\) si yo he recibido \(y_j\)? Aquel de todos los posibles símbolos que se hayan podido transmitir cuya probabilidad a posteriori sea máxima, será el que se decida como transmitido.

\[hat{x}_i = \max{ \{ P\left( x_i | y_j \right) \} }\]

Pero sin embargo, para poder determinar esta probabilidad, al decodificador se le tiene que pasar la información del canal. En lugar de recibir del canal directamente si se ha enviado un 1 ó un 0 (salida hard), el decodificador tiene como entrada la información del espacio de la señal, es decir, con el valor analógico de la salida del canal. De esta manera el decodificador dispone de mayor información para tomar la decisión de cuál ha sido el código transmitido. Este valor representa una medida de la fiabilidad con la que es tomado cada símbolo y se conoce como un codificador con entrada soft.

Un modo de calcular estas probabilidades a posteriori de manera computacionalmente eficiente, es mediante el algoritmo BCJR (ideado por Bahl, Cocke, Jelinek y Raviv en el 1974). El algoritmo BCJR permite la obtenención de las LLR a posteriori (Log Likelihood Ratio) por bit (\(x_i\) binario):
\[LLR \left( x_i | y \right) = \ln{\frac{P \left( x_i = 1 | y \right) }{P \left( x_i = 0 | y \right)}}\]

Mediante el uso de la LLR, si la probabilidad de haber transmitido \(x_i = 1\) habiendo recibido y es mayor que la de haber transmitido \(x_i = 0\), el valor del logaritmo es positivo y negativo al contrario. Por eso, es más sencillo mirar el signo de la operación, que comparar directamente el valor absoluto de los dos valores.

A diferencia del algoritmo de Viterbi, el BCJR calcula de manera diferente cuál ha sido el bit más probable que se ha podido transmitir. Viterbi calculaba decisiones hard a partir de la detección de la secuencia de símbolos más probable, es decir, aquella que supusiera un recorrido cuya distancia a través del Trellis fuera de menor distancia. Sin embargo, el algoritmo BCJR calcula información soft. Por tanto, mientras que el algoritmo de Viterbi produce la secuencia de símbolos más probable que minimiza la probabilidad de error por secuencia, el algoritmo BCJR minimiza la tasa de error media por símbolo. Esta sutil diferencia hace que la detección de códigos mediante BCJR sea más ventajosa que utilizando el algoritmo de Viterbi.

Ambos algoritmos comparten similitudes ya que ambos están basados en el mismo diagrama de Trellis, ambos asignan una métrica a cada transición y ambos recorren el diagrama de trellis recursivamente. Sin embargo, en lugar de pasar de principio a final como hace Viterbi, en el algoritmo BCJR se realizan dos pasadas: de izquierda a derecha y de derecha a izquierda.
Revisando el diagrama de trellis

Imaginemos que estamos en la etapa i de un diagrama de trellis cualquiera tal y como aparece en la figura. En el diagrama de trellis podemos definir el dato que entrega el canal como \(y_i\). El estado antes de la transición como \(S_{i-1}\) y el diagrama después de la transición como \(S_i\).

 

La clave del algoritmo BCJR es la descomposición de la probabilidad a posteriori para una transición en tres factores: el primero dependiendo de las observaciones pasadas, el segundo dependiendo de observaciones presentes y el tercero atendiendo a observaciones futuras. Cabe destacar que cuando se aplica el algoritmo BCJR el receptor ya dispone de la trama completa de datos, por lo que es posible separar entre pasado, presente y futuro. Es decir, dado un bit en la posición i, el algoritmo sí tiene acceso a los bits que se enviaron después de él. No es un algoritmo que se aplica al vuelo conforme llegan bits.

La decodificación de un símbolo mediante el criterio MAP viene dada por:
\[hat{x}_i = \max_{x_i \in \left \{ \text{M simbolos} \right \} }{ \{ P\left( x_i | y \right) \}} = \left \{ \text{Teorema de Bayes} \right \} = \\ \max_{x_i \in \left \{ \text{M simbolos} \right \} }{ \frac{ p \left( x_i, y \right) }{ p \left( y \right) } } = \left \{ p \left( y \right) \text{ constante} \right \} = \\ \max_{x_i \in \left \{ \text{M simbolos} \right \} }{ \{ p \left( x_i, y \right) \} } = \max_{l \in \left \{ 0, 1, …, M -1 \right \} }{\sum_{\left( S_{i-1}, S_i \right) \in S^l } p\left( S_{i-1}, S_i, y \right) } donde l \in \left \{ 0, 1, …, M -1 \right \}\]

son todos los posibles símbolos \(y \left( S_{i-1}, S_i \right) \in S^l\) son las posibles transiciones que salen de cada estado cuando se envía el símbolo l. Por ejemplo, en una comunicación binario los únicos símbolos son 0 y 1 (es decir \(l \in \left \{ 0, 1 \right \}\) que en este caso se llaman bits). Por tanto, de cada estado que tenga el decodificador saldrán dos posibles transiciones. Una debida a la recepción del bit 0 y otra al bit 1. El conjunto de transiciones que ocurren cuando se recibe un 0 se denominan \(S^0\) y las transiciones que ocurren cuando al decodificador entra un 1 se denominan \(S^1\).

Por tanto, de la ecuación \(\ref{eq:limite}\) podemos interpretar que para estimar el símbolo \(\hat{x}_i\) con mayor probabilidad de haber sido enviado, hay que encontrar la suma de las probabilidades conjuntas de estar en el estado \(S_{i-1}\), ir al estado \(S_i\) y haber recibido los datos y para cada uno de los símbolos. Aquel símbolo que de una suma mayor, será el elegido como \(\hat{x}_i\).

Como hemos dicho antes, podemos descomponer el diagrama de estados en tres: pasado, presente y futuro. De este modo, los datos recibidos y se pueden descomponer como los datos recibidos en el pasado, el bit recibido en el presente y los bits que me llegarán en el futuro.
\(\mathbf{y}=\left [ \left ( y_1~y_2~…~y_{i-1} \right ), y_i, \left (y_{i+1}~y_{i+2}~…~y_{N} \right ) \right ] = \left[ \mathbf{y}_1^{i-1}, y_i, \mathbf{y}_{i+1}^N \right] \) donde \(\mathbf{y}_1^{i-1}\) representa a los datos recibidos en el pasado, \(y_i\) es el dato que recibo en el presente y \(\mathbf{y}_{i+1}^N\) son los datos que recibiré en el futuro. El subíndice 1 indica pasado, el i indica presente y el i+1 indica futuro. Así mismo, el superíndice i-1 indica la dimensión del vector. En el caso de los datos pasados el vector tendrá una dimensión desde 1 hasta i-1 (por tanto dimensión i-1). Para el caso de los datos futuros, la dimensión va desde i+1 hasta N, que es el número de símbolos enviados (o bits si la señal es binaria).

Ahora, a partir de la interpretación que hemos hecho arriba de la función densidad de probabilidad \(p\left( S_{i-1}, S_i, y \right)\), podemos aplicar el teorema de Bayes repetidas veces para conseguir una función equivalente pero separada en varios trozos.

A modo de recordatorio:
\[p\left( a, b \right) = p \left(a | b\right) p \left( b \right)\]
\[p\left( a, b, c \right) = p \left(a | b, c\right) p \left( b, c \right)\]
\[p\left( a, b, c, d \right) = p \left(a, b | c, d\right) p \left(c, d \right)\]

Teniendo en cuenta esto, podemos reescribir la función \(p\left( S_{i-1}, S_i, y \right)\) como, \(p\left( S_{i-1}, S_i, \mathbf{y} \right) = p \left( S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i, \mathbf{y}_{i+1}^N \right)= p \left(\mathbf{y}_{i+1}^N | S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i \right) p \left( S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i \right) \)

En \(\ref{eq:prob_states_1}\) hemos descompuesto los datos recibidos en pasado, presente y futuro. Después hemos escrito la misma función utilizando la probabilidad condicionada de los datos futuros \(\mathbf{y}_{i+1}^N\). Ahora volvemos a aplicar la probabilidad condicionada en el factor \(p \left( S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i \right)\) pero esta vez evaluando la expresión a llegar al estado \(S_i\) y que el dato sea \(y_i\) si estábamos en el estado \(S_{i-1}\) y los datos pasados son \(y_1^{i-1}\). \(p \left(\mathbf{y}_{i+1}^N | S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i \right) p \left( S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i \right) = \\ p \left(\mathbf{y}_{i+1}^N | S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i \right) p \left(S_i, y_i | S_{i-1}, \mathbf{y}_1^{i-1} \right)p \left(S_{i-1}, \mathbf{y}_1^{i-1} \right)\)

Ahora fijémonos en cada término: \(p \left(S_{i-1}, \mathbf{y}_1^{i-1} \right)\): es la probabilidad conjunta de haber estado en el estado \(S_{i-1}\) y que los datos anteriores fueran \(\mathbf{y}_1^{i-1}\). A esta expresión la vamos a llamar \(\alpha_{i-1} \left( S_{i-1} \right)\).

\(p \left(S_i, y_i | S_{i-1}, \mathbf{y}_1^{i-1} \right)\): es la probabilidad de llegar al estado \(S_i\) con el símbolo \(y_i\) sabiendo que estábamos en el estado \(S_{i-1}\) y que se habían enviado anteriormente \(y_1^{i-1}\). Obviamente, los datos que se habían enviado anteriormente no van a afectar en dónde estaré posteriormente si ya sé que estaba en \(S_{i-1}\). Una analogía fácil para visualizar este hecho es el siguiente: yo estoy en el punto A. Desde este punto tengo dos caminos que puedo elegir: el camino 1 y el camino 0. Para determinar si voy a terminar en el punto B o no, me da exactamente igual saber qué camino escogí para llegar al punto A. Solo me importa donde estoy, dónde voy y qué caminos tengo para elegir en ese momento. Por tanto, la expresión \(p \left(S_i, y_i | S_{i-1}, \mathbf{y}_1^{i-1} \right)\) se puede simplificar como \(p \left(S_i, y_i | S_{i-1}\right)\). A esta expresión la vamos a llamar \(\gamma_{i} \left( S_{i-1}, S_i \right)\).
\(p \left(\mathbf{y}_{i+1}^N | S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i \right)\): siguiendo la analogía anterior, esta expresión representa cuál es la probabilidad de que escoja en el futuro una serie de caminos si ya sé dónde estoy, dónde estuve anteriormente, los caminos que escogí en el pasado y el camino que he cogido para llegar a dónde estoy ahora mismo. En este caso, poco importa saber dónde estuve hace tiempo, saber cómo llegué allí o cómo he llegado a donde estoy, ya que solo saber dónde estoy ahora mismo (\(S_i\)) me da información de adónde puedo ir \(\mathbf{y}_{i+1}^N\), cuáles serán los bits que me pueden llegar en el futuro). Por tanto, esta expresión la podemos simplificar como \(p \left(\mathbf{y}_{i+1}^N | S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i \right) = p \left(\mathbf{y}_{i+1}^N | S_i, y_i \right)\). A esta expresión la vamos a llamar \( \beta_{i} \left( S_{i} \right)\).

De esta forma podemos redefinir el diagrama de trellis como muestra la siguiente figura.

trellis_splited
Amarillo 0, azul 1

Así podemos calcular cuanto valdría P\left( x_i = 0 | \mathbf{y}\right):
\[P\left( x_i = 0 | \mathbf{y}\right) = \sum_{\left( S_{i-1}, S_i \right ) \in S^0}{p\left( S_{i-1}, S_i, \mathbf{y} \right )} = \\ = \sum_{\left( S_{i-1}, S_i \right ) \in S^0}{\alpha_{i-1}\left(S_{i-1} \right) \gamma_i \left(S_{i-1}, S_i \right ) \beta_i \left(S_i \right )} = \\ =\alpha_{i-1}\left(a \right ) \gamma_i \left(a, a \right ) \beta_i \left(a \right ) + \alpha_{i-1}\left(b \right ) \gamma_i \left(b, a \right ) \beta_i \left(a \right ) + \\ + \alpha_{i-1}\left(c \right ) \gamma_i \left(c, b \right ) \beta_i \left(b \right )+ \alpha_{i-1}\left(d \right ) \gamma_i \left(d, b \right ) \beta_i \left(b \right )\]

Para calcular \(P\left( x_i = 1 | \mathbf{y}\right)\) sería igual pero utilizando las otras transiciones. O también \(P\left( x_i = 1 | \mathbf{y}\right) = 1 – P\left( x_i = 0 | \mathbf{y}\right)\)
Cálculo de α
\[\alpha_i \left( S_i \right) = p \left(S_i, y_1^i \right ) =\sum_{S_{i-1}, S_i \in S}{p \left(S_{i-1}, S_i, \mathbf{y}_1^{i-1}, y_i \right)} = \\ = \sum_{S_{i-1}, S_i \in S}{p \left( S_i, y_i| S_{i-1}, \mathbf{y}_1^{i-1} \right)}p \left(S_{i-1}, \mathbf{y}_1^{i-1} \right) = \\ = \sum_{S_{i-1}, S_i \in S}{p \left( S_i, y_i| S_{i-1} \right )}\alpha_{i-1} \left(S_{i-1} \right) = \sum_{S_{i-1}, S_i \in S}{\alpha_{i-1} \left( S_i \right)}\gamma_{i} \left(S_{i-1}, S_i \right)\]

Donde \(1 \leq i \leq N\) y \(N\) el número de símbolos enviados. Como vemos es un cálculo recursivo, en el que necesitamos el valor del α anterior para calcular el siguiente.
Cálculo de β
\[\beta_{i-1} \left(S_{i-1} \right ) = p \left(\mathbf{y}_i^n |S_{i-1} \right ) = \sum_{S_{i-1}, S_i \in S}{p \left( y_i,\mathbf{y}_{i+1}^n, S_i |S_{i-1}\right )}= \\ =\sum_{S_{i-1}, S_i \in S}{p \left(\mathbf{y}_{i+1}^n | S_i, y_i, S_{i-1}\right ) p \left( S_i, y_i| S_{i-1}\right )} =\\=\sum_{S_{i-1}, S_i \in S}{p \left(\mathbf{y}_{i+1}^n | S_i\right ) p \left( S_i, y_i| S_{i-1}\right )} =\sum_{S_{i-1}, S_i \in S}{\beta \left(S_i\right ) \gamma_i\left( S_{i-1}, S_i\right )}\]
Cálculo de γ
\[\gamma_i \left(S_{i-1}, S_i \right ) = p \left(S_i, y_i | S_{i-1} \right ) =p \left(y_i | S_i, S_{i-1} \right ) p \left(S_i | S_{i-1} \right) = p \left(y_i | c_i \right ) p \left( x_i \right )\]

Como podemos ver y era de esperar, el parámetro \(\gamma\) depende del canal, ya que \(p \left(y_i | c_i \right )\) es la función de verosimilitud que caracteriza el canal. En caso de ser un canal gaussiano, la \(p \left(y_i | c_i \right ) = \frac{1}{\sigma^2 \sqrt{\pi}} \cdot e^{- \frac{\left| y_i – c_i \right|^2}{2\sigma^2} }\)

Redes de transporte de primera generación

En las redes de transporte de Internet, para poder enviar grandes cantidades de información se utilizan unos protocolos especiales. Uno de ellos es SDH.

Synchronous Digital Hierarchy (SDH)

Una trama de SDH tiene una duración de 125 μs. Dentro de estos 125 μs se puede añadir información de muchos tributarios (usuarios). El número de tributarios que es posible añadir está estandarizado, de manera que las tasas binarias en SDH son:

Tasa binaria SDH Interfaz óptico Tasa binaria
STM-1 OC-3 155 Mbps
STM-4 OC-12 622 Mbps
SMT-16 OC-48 2.5 Gbps
STM-64 OC-192 10 Gbps
STM-256 OC-768 40 Gbps

Capas SDH

En el protocolo SDH existen 4 capas:

  1. Path Layer: establece conexiones end-to-end.
  2. Multiplex Section Layer: tareas de multiplexado, sincronización y protección.
  3. Regeneration Section Layer: genera las tramas y mantenimiento de la sección. Introduce o extrae los Virtual Containers.
  4. Photonic Layer: interfaz óptico por donde viaja la información

sdh_layerLas tramas de 125 μs están construidas por el contenedor básico llamado STM (Synchronous Transport Module), en el que cada uno equivale a 64 kbps.

stm

El STM-1 tiene una tasa binaria de:
\[270 \cdot 1 \cdot 9 \cdot 64~kbps = 155~Mbps\]
El STM-4:
\[270 \cdot 4 \cdot 9 \cdot 64~kbps = 622~kbps\]
La trama SDH está formada por la cabecera y la payload.
trama
Cabecera

  • Puntero con información de señalización y monitorización.
  • Información APS (Automatic Protection Switching).
  • Información sobre la estructura de la traba

Payload

También tiene una cabecera para señalización y medida de errores.
El container es la unidad básica de empaquetamiento.
Cabecera (llamada POH) + Container = Virtual Container
Los VC pueden ser de orden alto (alta velocidad) o de orden bajo (baja velocidad).
Hay containers de diferente tamaño que se tienen que encajar dentro del espacio de la payload (C2, C12, C11).
Los VC pueden ser creados por elementos no bien sincronizados, por lo que no se podrán añadir a tiempo dentro de la trama STM.

La cabecera estaba formada por un hueco llamado pointer. Este puntero se rellena con información que junto con un VC forma la unidad administrativa.
Todo el bloque de puntero + Todas las unidades administrativas = Grupo de unidad administrativa
¿Qué pasa con el tráfico de baja velocidad? ¿Cómo se puede poner dentro de la trama STM?
Con unidades tributarias = puntero + VC de bajo orden.
Varias unidades tributarias generan un grupo de unidades tributarias.

La unidad tributaria es igual que la unidad administrativa pero más pequeña. Son de VC de bajo orden.

Por orden de tamaño ascendente:

  1. Container
  2. Virtual container (VC): POH + C-n
  3. Unidad tributaria (TU): puntero + VC de bajo orden
  4. Unidad administrativa (AU): puntero + VC de alto orden
  5. Grupo de unidades tributarias (TUG): n · TU
  6. Grupo de unidades administrativas (AUG): n · AU
  7. STM-N: SOH + AUG

sdh_2

Generic Frame Procedure (GFP)
Para poder añadir información de diferentes protoclos (Ethernet, Fibre Channel, …) en una trama SDH se utiliza un procedimiento de entramado genérico. De esta manera SDH puede transportar cualquier protocolo sin importar su procedencia.
Concatenación SDH

Cuando se quiere transportar información mayor que la unidad básica, se pueden combinar varios contenedores. Por ejemplo añadir 599.04 Mbps utilizando 4 VC-4c. Sin embargo, de esta manera se desperdicia mucho espacio. Por eso, dos contenedores se enlazan mediante código y no físicamente contiguos. Esto se conoce como Virtual Concatenation Group (VCG).

Existe un método conocido como Link Capacity Adjustment Scheme (LCAS) que varía dinámicamente el ancho de banda de los contenedores de una concatenación virtual sin afectar al servicio.

Optical Transport Network (OTN)

Es un protocola (más bajo que SDH) para interconectar distintas redes de transporte. Al principio, cada fabricante lo hacía de manera diferente y por tanto no se podían interconectar entre sí.

OTN Tasa binaria Equivalente SDH
OTU-1 2.5 Gbps STM-16
OTU-2 10 Gbps STM-64
OTU-3 40 Gbps STM-256
OTU-4 100 Gbps

Esquemas de protección

En SDH, el tiempo máximo de recuperación por un fallo del sistema sin afectar al funcionamiento de este es de 60 ms: 50 ms para la conmutación de protección y 10 ms para detectar el fallo.

Conceptos básicos
Tipos de protección

  • Protección dedicada: cada conexión activa tiene asignado su propio ancho de banda para reencaminar el tráfico en caso de fallo.
  • Protección compartida: un ancho de banda de protección para múltiples conexiones activas.

También pueden considerarse:

  • No reversibles: la conmutación de vuelta al camino activo original una vez se ha reparado un fallo es manual
  • Reversibles: cuando se detecta que el falo ha sido reparado, se conmuta automáticamente a la fibra original.

Otras taxonomías:
tax
a) Uso normal b) Unidireccional c) Bidireccional

Para notificar fallos en la red se utiliza el protocolo APS (Automatic Protection Switching).

Tipos de conmutación

conm

  1. Camino activo normal
  2. Conmutación de camino (path-switching): la conmutación de caminos se hace a nivel de path, de manera que la conexión se establece por un camino alternativo de punto a punto.
  3. Span protection: existe un camino replicado y paralelo al que ha dejado de funcionar.
  4. Conmutación de anillo. Solo se redirecciona en los nodos cuyo enlace no funciona. A diferencia del path-switching, este reencaminamiento no es punto a punto, sino que solo se da en el momento en el que el siguiente enlace no funciona.

1+1: el tráfico se transmite simultaneamente sobre dos fibras separadas.
1:1: hay 2 fibras pero el tráfico solo se envía por una. Se conmuta en caso de fallo y se notifica mediante APS (Automatic Protection Switching).
1:N: N fibras comparten un único camino de protección.

Anillos autoreparables, arquitecturas
SNCP: subnetwork connection protection
Anillo unidireccional. La fibra de protección se transmite en otro sentido. No necesita protocolo de señalización.

MS-SPRing: Multiplex Section Shared Protection Ring
El enrutamiento se realiza por el camino más corto. Hay 2 ó 4 fibras de protección.
Hay reutilización espacial: mientras la fibra auxiliar no falle, se puede aprovechar entre múltiples conexiones. Son anillos bidireccionales.

En MS-SPRing/4 hay 4 fibras. Si falla el enlace en una dirección hay una de reserva.
En MS-SPRing/2 hay 2 fibras. Cada una utiliza la mitad del ancho de banda. En caso de fallo todo el tráfico va por la otra (los dos sentidos de transmisión por la misma fibra).

Extracción de parámetros para reconocimiento de voz

A la hora de conformar una vocal, las cuerdas vocales vibran, produciendo una vibración en el aire en forma de tren de pulsos. A su vez, este tren de pulsos pasa a través del tracto vocal, el cual, dependiendo del fonema que se ha pronunciado actuará como un filtro y modificará las componentes frecuenciales del tren de pulsos.

reconPara cada fonema, el filtro resultante del tracto vocal es diferente. Mientras hablamos, el responsable de que una palabra o un fonema suene de una manera particular es el tracto vocal. Se puede ver un ejemplo intentando decir ‘Hola’ con la boca cerrada o sin mover la boca. Aunque las cuerdas vocales vibran de la misma manera que lo hacen cuando la boca está abierta, no se entiende bien.

Por tanto, en reconocimiento de voz es interesante poder extraer características del tracto vocal, que corresponde a la forma de la envolvente del sonido visto desde el punto de vista frecuencial. Entonces, ¿cómo extraemos la envolvente? Utilizando el análisis cepstral.

Análisis cepstral

El cepstrum de una señal es el resultado de calcular la transformada de Fourier (FT, del inglés Fourier Transform) del espectro de la señal estudiada en escala logarítmica (dB). El nombre cepstrum deriva de invertir las cuatro primeras letras de spectrum. El cepstrum puede ser visto como una información del ritmo de cambio de las diferentes bandas de un espectro.

Wikipedia

El cepstrum o análisis cepstral aplicado a una voz equivale a:
La voz \(V\left(f\right) = P \left(f \right) \cdot T\left( f \right)\), donde \(P \left(f \right)\) son las componentes frecuenciales producidas por las cuerdas vocales y \(T \left(f \right)\) es el filtro equivalente del tracto vocal.
Si aplicamos logaritmo a \(V\left( f\right)\), tenemos \(\log{\left[ V\left( f \right) \right]} = \log{\left[ P\left( f \right) \right]} + \log{\left[ T\left( t \right) \right]}\)

La transformada de Fourier de \(T\left( f \right)\) tiene bajas frecuencias. Por otra parte, la transformada de Fourier de \(P\left( f \right)\) contiene altas frecuencias.
De esta manera, si volvemos a hacer la transformada de Fourier de \(V\left( f \right)\) y nos quedamos solo con las bajas frecuencias, habremos eliminado la componente de \(P\left( f \right)\) y tendremos solo la información de \(T\left( f \right)\). Hay que pensar en el concepto de Transformada de Fourier de la Transformada de Fourier \(\left(FFT\left\{ FFT \left\{ x \right\} \right\} \right)\).

Sin embargo, en audio no se utiliza exactamente de esta manera, si no que además se aplica un banco de filtros Mel y se utiliza como segunda transformada la DCT en lugar de la transformada de Fourier debido a sus propiedades de compresión de información (consigue concentrar la mayor parte de la información en pocos coeficientes). Es por ello que en la extracción de parámetros para reconocimiento de voz se utilizan los Mel Frequency Cepstral Coefficients (MFCC).

Mel Frequency Cepstral Coefficients (MFCC)

Para empezar a extraer los parámetros MFCC de un archivo de audio, el audio se enventana. En lugar de extraer los parámetros MFCC de una pieza de 1 segundo (que podría ser aproximadamente la duración de una palabra), se extraen los MFCC de segmentos de 25 ms. La ventana es una ventana de tipo Hanning o Hamming para evitar los armónicos artificiales que aparecen al utilizar ventanas cuadradas. Cada ventana se desplaza 10 ms en tiempo, por lo que existe una superposición entre una ventana y otra.Enventanado MFCC

  1. A cada una de estas ventanas se aplica la FFT.
  2. A la FFT de cada ventana se le aplica el banco de filtros MEL.
  3. Se calcula el logaritmo
  4. Se hace la transformada discreta del coseno (DCT)
  5. Nos quedamos con los N primeros coeficientes (baja frecuencia). Normalmente 12 coeficientes. O también 12 para bajas frecuencias y 1 para la energía media de la ventana.

Como ya habíamos dicho la diferencia entre los cepstrum y los MFCC, es que una vez hecho la FFT en el paso 1, se filtra mediante los filtros MEL y en lugar de aplicar en el paso 4 otra vez una FFT, se aplica la DCT .

Cabe destacar que en reconocimiento de voz no se reconocen fonemas sino palabras. Por tanto, para un archivo de audio de 1 segundo, tendremos \(\frac{1~seg-25~ms}{10~ms} = 97.5 \approx 98\)

parámetros MFCC \(\left( \left\{p_1, p_2, …, p_m \right\} \right)\) y cada parámetro MFCC contendrá N elementos \(\left( p_m = \left\{c_1, c_2, …, c_N \right\} \right)\).

Función para la extracción de MFCC en MATLAB de Kamil Wojcicki

Parámetros típicos de la función mfcc.m:

Si la señal por ejemplo está grabada a 16 kHz, tiene componentes frcuenciales hasta 8 kHz. 7 kHz como frecuencia superior máxima es una buena elección para no tener en cuenta las frecuencias próximas a Nyquist que suelen verse atenuadas y porque hasta 7 kHz hay suficiente información de voz. Por debajo se puede limitar a 50 Hz, dado que la voz humana no tiene apenas información por debajo y así se puede evitar ruidos.

Amplificador de instrumentación con 3 AO

ia-with-three-oa
Amplificador de instrumentación con 3 AO

En esta disposición de AI la podemos separar en dos etapas:

Etapa 1stage-1Por superposición y haciendo el KCL:
\[ V_{in_1} = 0 \]
\[R_3 + \alpha R_4 = R_G\]
\[ \frac{V_{in_2}-V_B}{R_5} = \frac{0-V_{in_2}}{R_G}\]
\[V_{in_2}R_G -V_B R_G =-V_{in_2}R_5\]
\[V_B = \left( 1 + \frac{R_5}{R_G} \right) V_{in_2}\]
\[\frac{0 – V_{in_2}}{R_G} + \frac{0 – V_A}{R_2} = 0\]
\[V_A = – \frac{R_2}{R_G}V_{in_2}\]

Ahora con \(V_{in_2} = 0\),

\[\frac{V_{in_1}-0}{R_G} = \frac{0-V_B}{R_5} \]

\[V_{in_1}R_5 =-V_B R_G\]

\[V_B =-\frac{R_5}{R_G}V_{in_1}\]

\[\frac{0 – V_{in_2}}{R_G} + \frac{0 – V_A}{R_2} = 0 \]

\[V_A = \left( 1 + \frac{R_2}{R_G} \right) V_{in_1} \]

Por lo que finalmente, tenemos que:
\[V_A = \left( 1 + \frac{R_2}{R_G} \right) V_{in_1} – \frac{R_2}{R_G}V_{in_2} \]

\[V_B = \left( 1 + \frac{R_5}{R_G} \right) V_{in_2} -\frac{R_5}{R_G}V_{in_1} \]

En cuanto a la ganancia en modo común de esta etapa, la podemos calcular haciendo \(V_{in_1} = V_{in_2}\). De esta manera,
\[V_A = \left( 1 + \frac{R_2}{R_G} \right) 1 – \frac{R_2}{R_G}1 = 1 \]
\[V_B = \left( 1 + \frac{R_5}{R_G} \right) 1 -\frac{R_5}{R_G}1 = 1\]

Por tanto, sea cual sea el valor de \(R_2\), \(R_5\) y \(R_G\), una tensión en modo común va a pasar sin modificarse a \(V_A\) y \(V_B\).

La segunda etapa restante es:
De nuevo, aplicando superposición podemos llegar a la expresión final de la salida.
\[V_{out} = – \frac{R_9}{R_1} V_A + \left(1 + \frac{R_9}{R_1} \right) \frac{\beta R_8 + R_7 }{ \beta R_8 + R_7 + R_6} V_B \]
\[V_{out} = – \frac{R_9}{R_1} \left[ \left( 1 + \frac{R_2}{R_G } \right) V_{in_1} – \frac{R_2}{R_G }V_{in_2} \right]+ \left(1 + \frac{R_9}{R_1} \right) \frac{\beta R_8 + R_7 }{ \beta R_8 + R_7 + R_6} \left[ \left( 1 + \frac{R_5}{R_G} \right) V_{in_2} -\frac{R_5}{R_G}V_{in_1} \right] \]

Para calcular la ganancia en modo común de esta etapa vamos a aplicar una tensión igual en las dos entradas diferenciales. Por tanto \(V_A = V_B = V_{CM}\). Así conseguimos la siguiente expresión para la tensión de salida.
\[V_{out} = – \frac{R_9}{R_1} V_{CM} + \left(1 + \frac{R_9}{R_1} \right) \frac{\beta R_8 + R_7 }{ \beta R_8 + R_7 + R_6} V_{CM} \]

Vamos a identificar \(R_{ref} = \beta R_8 + R_7\)

para facilitar las operaciones.
\[V_{out} = – \frac{R_9}{R_1} V_{CM} + \left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref}}{R_{ref}+ R_6} V_{CM} \]

Idealmente, querríamos que esta tensión fuese igual a 0, de manera que el amplificador de instrumentación pudiese rechazar completamente para poder, por ejemplo, eliminar el ruido acoplado en ambas entradas tal y como puede verse en la figura.

CMRR

\[V_{out} = – \frac{R_9}{R_1} V_{CM} + \left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref}}{R_{ref}+ R_6} V_{CM} \]
\[G_{cm} = \frac{V_{out}}{V_{CM} } = – \frac{R_9}{R_1} + \left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref}}{R_{ref}+ R_6} \]
\[G_{cm} = \frac{R_{ref}}{R_{ref}+R_6} – \frac{R_9 R_6}{R_1 R_{ref} + R_1 R_6} \]

Por tanto, resolviendo la ecuación de ganancia en modo común igual a 0, \(G_{cm} = 0\), tenemos que
\[- \frac{R_9}{R_1} + \left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref}}{R_{ref}+ R_6} = 0\]

Ecuación que podemos identificar de la siguiente manera:
\[A = \frac{R_9}{R_1}\]
\[ B =\frac{R_{ref}}{R_{ref}+ R_6}\]
\[ – A + \left(1 + A \right) B = 0 \]
\[-A + B + AB = 0 \]
\[\left( B -1 \right) A = -B \]
\[ A = \frac{-B}{B-1} = \frac{B}{1-B} \]
\[A = \frac{\frac{R_{ref}}{R_{ref}+ R_6}}{1-\frac{R_{ref}}{R_{ref}+ R_6}} = \frac{R_{ref}}{R_6} \]
\[\frac{R_9}{R_1} = \frac{R_{ref}}{R_6} \]

Por tanto, \(R_9 = R_{ref}\) y \(R_{1} = R_6\)

Por otra parte, si queremos que \(V_A = V_B\) cuando \(V_{in_1} = V_{in_2}\), necesitamos que se cumpla la siguiente relación entre las resistencias \(R_2\) y \(R_5\).
\[ V_A = V_B \left( 1 + \frac{R_2}{R_G} \right) V_{in_1} – \frac{R_2}{R_G}V_{in_2} = \left( 1 + \frac{R_5}{R_G} \right) V_{in_2} -\frac{R_5}{R_G}V_{in_1} \left( 1 + \frac{R_2}{R_G} \right) – \frac{R_2}{R_G} = \left( 1 + \frac{R_5}{R_G} \right) -\frac{R_5}{R_G} \]

Por simple inspección se llega a la conclusión de que \(R_2 = R_5\).

Por último, podemos calcular la ganancia diferencial y la ganancia de modo común de otro modo. Si definimos la tensión diferencial como \(V_d = V_{in_1}- V_{in_2}\) y la tensión en modo común como \(V_c = \frac{V_{in_1} + V_{in_2}}{2}\), sustituimos en la expresión de \(V_o\) e identificamos la expresión resultante como \(V_o = G_d V_d + G_c V_c\), en la que \(G_d\) es la ganancia diferencial y \(G_c\) es la ganancia en modo común (la misma que hemos calculado arriba, obtenemos que \(G_d\) es igual a:
\[ G_d = \frac{-2 R_9 R_G R_{ref} – R_9 R_G R_6 – 2 R_9 R_2 R_{ref} -2 R_9 R_2 R_6 – R_{ref}R_1 R_G – 2 R_1 R_5 R_{ref} – 2 R_9 R_{ref} R_5}{2R_1 R_G \left( R_{ref} + R_6 \right) } \]

En caso de tener las resistencias balanceadas, la expresión de la ganancia diferencial se simplifica a:
\[G_d = -\frac{R_G^2}{R_1 \left( R_9 + R_1 \right)} – \frac{R_9}{2 \left( R_9 + R_1\right)} – \frac{R_9 R_2}{R_G \left( R_9 + R_1 \right)} – \frac{R_9}{2 \left( R_9+ R_1 \right)} – \frac{R_2 R_9}{R_G \left( R_9+ R_1 \right)} – \\ \frac{R_9^2 R_2}{R_1 R_G \left( R_9 + R_1 \right)} \]

Conclusiones

Este amplificador de instrumentación es muy utilizado para amplificar salidas de sensores y demás aplicaciones de instrumentación. Sus ventajas residen en que si está balanceado se puede conseguir tener una ganancia de modo común baja (o CMRR alto) a la vez que se mantiene una ganancia ajustable a través de \(R_G = R_3 + \alpha R_4\). Por último, también podemos ver como la impedancia de entrada de este amplificador es muy alta, ya que ambas entradas son las entradas de un AO.
\[V_{out} = – \frac{R_9}{R_1} \left[ \left( 1 + \frac{R_2}{R_G } \right) V_{in_1} – \frac{R_2}{R_G }V_{in_2} \right]+\left(1 + \frac{R_9}{R_1} \right) \frac{R_{ref} }{R_{ref} + R_6} \left[ \left( 1 + \frac{R_5}{R_G} \right) V_{in_2} -\frac{R_5}{R_G}V_{in_1} \right] \]

\[G_d = \frac{-2 R_9 R_G R_{ref} – R_9 R_G R_6 – 2 R_9 R_2 R_{ref} -2 R_9 R_2 R_6 – R_{ref}R_1 R_G – 2 R_1 R_5 R_{ref} – 2 R_9 R_{ref} R_5}{2R_1 R_G \left( R_{ref} + R_6 \right)} \]
\[G_{cm} = \frac{R_{ref}}{R_{ref}+R_6} – \frac{R_9 R_6}{R_1 R_{ref} + R_1 R_6} \]

Enlaces a CircuitLab para simulación



Módulo programador para ESP8266

En anteriores entradas he hablado del ESP8266, de cómo configurarlo y cómo introducirnos un poco en el amplio espectro de posibilidades que este pequeño chip nos brinda. No obstante, en la documentación que he publicado falta una parte importante, y esta es la de hardware.

Dado que el pinout del ESP-01 no es apto para conectarlo a una protoboard (a no ser que quieras cortocircuitarlo) decidí hacer un pequeño adaptador para conectar el programador serie (FTDI) y la alimentación, así como añadir un jumper para iniciar el chip en modo programación.

Para ello he utilizado:

  • Tira doble de cuatro pines hembra (o dos tiras simples de cuatro pines hembra), para la conexión del ESP.
  • Tira de 6 pines macho, para la conexión del ESP y salidas de GPIO.
  • Tira de 2 pines hembra, para alimentación.
  • Dos pilas AAA (1.5V cada una)

Estos son los distintos pines disponibles en el ESP-01:

pinout
ESP-01 pinout

Las conexiones que he hecho en mi adaptador son las siguientes:

 

esp-conn
Conexiones adaptador

 

El número de pines corresponde al de los pines del ESP.

El resultado final es este:

programador
Adaptador ESP-01

También decir que la alimentación que utilizo son dos resistencias conectadas en serie, simplemente soldadas (podéis utilizar un portapilas, pero así es más barato):

power
Alimentación a 3V

Mobile IP

Mobile IP es un estándar de protocolo comunicación diseñado para permitir a usuarios de dispositivos móviles moverse entre redes manteniendo la misma dirección IP.

Se definen dos tipos de movilidad:

  1. Macro movilidad: el desplazamiento de un móvil se produce entre dos dominios de redes diferentes.
  2. Micro movilidad: el desplazamiento de un móvil se produce entre dos subredes de un mismo dominio administrativo.

Mobile IP está definida en el marco de macro movilidad.

En el estándar Mobile IP se añaden tres nuevas entidades funcionales:

  1. Mobile Node (MN): terminal que cambia de ubicación.
  2. Home Agent (HA): router de la home network que coge los datagramas destinados al Mobile Node y los entrega a través de la care-of address. También almacena información sobre la posición actual del MN.
  3. Foreign Agent (FA): router de una red visitada que ofrece servicios de enrutado al MN mientras el MN es registrado.

MIPMobile IP permite enrutar datagramas IP independientemente de la localización del dispositivo. Cada Mobile Node (MN) es identificado por su Home Address sin importar su ubicación. Mientras está fuera de la home network, se asocia una care-of address al MN que identifica su ubicación actual y la Home Address a la cual está asociado el extremo del tunel de encapsulamiento que conecta con el Home Agent.

En Mobile IP (MIP) se utilizan dos tipos de IP: estáticas y dinámicas.

La estática es la Home Address la que le proporciona la Home Network al dispositivo. Es la IP que siempre va a tener aun cuando el dispositivo salga de la Home Network, por tanto la dirección IP permanente.

La dinámica es la care-of address, la cual es proporcionada por la Foreign Network (FN) de manera que el Home Agent pueda seguir comunicándose con el dispositivo, por tanto la dirección IP temporal.

Por tanto recapitulando, el MN tiene una IP que lo identifica en internet: la Home Address. Sin embargo, cuando el MN cambia a una red externa a la cual tenía asociada la Home Address se le tiene que asignar una IP provisional que le permita al Home Agent reenviarle los datagramas IP que el MN tiene que recibir o transmitir. Por tanto, el Foreign Agent al cual está conectado le proporciona una care-of address. Con esta care-of address el MN se comunica directamente (mediante un encapsulamiento tunel) con el Home Agent. De esta manera, aun cuando el dispositivo no está conectado en la red original se simula que la dirección IP del MN no ha cambiado, ya que el Home Agent está haciendo de proxy.

Otro forma es que no exista exista el Foreign Agent, de manera que el túnel se establece directamente entre el MN y el HA. De esta manera el MN se le asigna una co-located care-of address.Mobile IP

 

Modo de funcionamiento

  1. El MN es el encargado de descubrir si está conectado a su Home Network o se ha movido a una Foreign Network. No son los únicos responsables en este proceso sino que también intervienen la Foreign Agent y el Home Agent. Este proceso se denomina Agent Discovery. Para esto se utiliza una extensión del protocolo ICMP.
  2. El siguiente paso es registrarse en la Foreign Network para obtener la care-of address. Este proceso intervienen tanto el MN, el FA así como el HA, que debe conocer la care-of address del MN registrado es un Home Network. El proceso de registro está formado por dos mensajes, una petición de registro y una respuesta a la petición. De esta manera se informa al HA de la CoA del MN.
  3. Una vez ya están las direcciones asignadas se puede transferir información a través del túnel que conecta el Home Agent y el Foreign Agent.

perfm

Proceso de registro
Proceso de registro

Si el túnel IP solo es unidireccional se crea un problema en el que hay un enrutado triangular que no permite que la IP del Mobile Node sea la misma para recibir que para transmitir. Haciendo el túnel bidireccional se soluciona el problema.

ip_triangular_problemtriangular_problem_solution

Proceso completo de Agent Discovery, Registro y transferencia de datos
Proceso completo de Agent Discovery, Registro y transferencia de datos

Algunas desventajas del Mobile IP es que es necesario incorporar el nodo de Foreign Agent en todas las redes visitadas. También sobrecarga la red al necesitar de peticiones constantes si el MN se mueve. Otra desventaja es que es necesario implementar procesos de autentificación entre el MN y el FA y entre el FA y el HA.

En GPRS y UMTS (2.5G y 3G), GGSN (Gateway GPRS Support Node) actúa como Home Agent y el SGSN (Serving GPRS Support Node) actúa como Foreign Agent.

En 4G el P-GW (Packet data network Gateway) actúa como Home Agent y el SGW (Serving Gateway) actúa como Foreign Agent.

Mobile IPv6

Hasta ahora, todo lo referente a Mobile IP aplicaba para IPv4. Para Mobile IPv6 existen ciertas diferencias.

Por ejemplo, en Mobile IPv6 no existe la entidad de Foreign Agent (FA) ya que ha sido sustituida por el Access Router (AR).

En IPv6 existen 3 entidades funcionales, el Home Agent (HA), el Mobile Node (MN) y el Correspondent Node (CN) que es la dirección de destino de los datos.

En IPv6 el Home Agent sigue haciendo de proxy para evitar el problema del tunelado triangular que hemos explicado anteriormente.

Sin embargo, existe un modo de operación llamado Route Optimization que establece un tunel directo entre el MN alojado en una Visited Network y el CN sin pasar por el Home Agent.

mipv6

Publicación del estado del enlace (LSA) en OSPF

En el protocolo OSPF se utilizan diferentes tipos de mensaje para informar a los routers de la composición de la red. Existen 5 tiempos diferentes de paquetes y cada uno de ellos tiene una función diferente:

  • Paquetes hello [Tipo 1]: se utilizan para comunicar a los routers adyacentes de la propia existencia. De esta manera, los routers conocen qué vecinos tienen, por lo que son la base para el intercambio de datos de enrutamiento en OSPF.
  • Paquetes de descripción de la base de datos [Tipo 2]: se utiliza para describir  el contenido de la base de datos de cada router. Sin embargo, no transporta la información que ella contiene.
  • Paquetes de solicitud del estado del enlace [Tipo 3]: se utiliza para solicitar fragmentos específicos de la base de datos del estado del enlace del router vecino. Después de recibir un actualización de la descripción de la base de datos de un vecino, es posible que un router descubra que la información del vecino es más nueva o más completa que la que el mismo posee. Para pedirle al router vecino que se la dé, se utiliza un paquete de solicitud del estado del enlace.
  • Paquete de actualización del estado del enlace [Tipo 3]: se utilizan para transportar realmente los datos de los estados del enlace a los routers vecinos. Exiten 5 tipos de paquetes:
    • LSA de router o Router Link (RL) [Tipo 1]: describen el estado de un interfaz de un router dentro de su área. Cada router genera un aviso de este tipo por cada enlace que tenga.
    • LSA de red o Network Link (NL) [Tipo 2]: es similar a un LSA de router, en la que se informa del coste y el estado del enlace para todos los routers conectados a la red, con la diferencia de que la información del LSA de red es un resumen de toda la información del estado del enlace y coste de la red (LAN) y no solo de sus vecinos. Solo el router designado (DR) puede generarla y seguirla.
    • LSA de resumen-red IP o Summary Link (SL) [Tipo 3]: se utiliza para describir información de alcanzabilidad de redes de otras áreas del mismo sistema autónomo (AS). Son generados por los router fronterizos (Area Border Router, ABR) del área y se transmiten hacia otras áreas. Sirven también para indicar la ubicación del Autonomous System Border Router (ASBR), el cual conecta el sistema autónomo con otro AS externo.
    • LSA de resumen-router límite de sistema autónomo [Tipo 4]: la diferencia entre la LSA de Tipo 3 y la LSA de Tipo 4 es que la LSA de Tipo 3 describre rutas entre áreas y la LSA de Tipo 4 describe rutas que son externas a la red OSPF.
    • LSA de AS externo o External Link (EL) [Tipo 5]: son generados por los ASBR en su AS y hacia otros AS y difundidas por el ASBR dentro del área 0 a través de todas las áreas OSPF a las que pertenece. Sirven para describir información de alcanzabilidad de destinos fuera del propio AS. Estos destinos pueden ser hosts específicos o direcciones de red externas.

Como vemos, el protocolo OSPF es altamente complejo y requiere una cantidad considerable de mensajes para su correcto funcionamiento.