Trabajo Práctico Especial

Demodulación de tonos de discado de teléfono

Nombre:

Padrón:

Requisitos para la aprobación

Este trabajo práctico debe ser entregado subiendo al campus en la fecha indicada un archivo comprimido que incluya todo el material relevante. Debe incluir al menos

  • Un link al documento de NextJournal publicado. Debe incluir los resultados pedidos con las explicaciones, imágenes, código y gráficos que considere pertinentes.

  • Un PDF del informe (puede imprimir la página en pdf)

Verifique que al ejecutar el notebook completo (opción `Run All`), el código corre correctamente y genera las salidas del notebook publicado y entregado.

(Si no lo hace en NextJournal, el archivo comprimido debe contener al menos las respuestas y gráficos solicitados junto el código usado.)

Todos los gráficos deberán tener título, comentarios en ambos ejes sobre la unidad a representar y el eje de absisas debe estar en unidades de tiempo o frecuencia según corresponda.

Introducción

El sistema de discado por tonos de nuestros teléfonos utiliza los principios de la codificación DTMF, o Dual Tone Multi Frequency. Este sistema de codificación convierte los códigos de información (los 10 dígitos decimales en el caso del discado) en otras tantas señales analógicas, cuya frecuencia debe estar contenida en el rango de las frecuencias de la voz humana, o más bien en el rango de frecuencias del canal telefónico. De este modo se crea un sistema de codificación que puede ser interpretado por todos los sistemas que se conectan a la red telefónica, como (obviamente) teléfonos, pero también, modems, máquinas de fax, centrales telefónicas, hubs, switches, y hasta las distribuidoras de televisión por cable.

La versión de DTMF utilizada en el discado delefónico se conoce con la marca registrada "Touch-Tone" y está estandarizada por la recomendsación Q.23 de la ITU-T. El código es muy sencillo y consiste en combinar dos tonos de distintas frecuencias (Dual Tone), de un número de frecuencias distintas (Multi Frequency) pero conocidaas y fijas elegidas según se indica en la tabla siguiente:

Código auxiliar

# Cargo paquetitos
using DSP, FFTW, Statistics, WAV
function wavread_mono(file)
  x, sr = wavread(file)
  return mean(x; dims=2)[:], sr
end
# Y armo un par de funciones auxiliares
stem(args...; kwargs...) = sticks(args...; 
  																marker=:circle, 
  																leg=false, 
  																kwargs...)
zeropolegain(pr) = DSP.ZeroPoleGain(pr)
zeropolegain(z, p, g) = DSP.ZeroPoleGain(z, p, g)
polynomialratio(zpg) = DSP.PolynomialRatio(zpg)
function polynomialratio(b, a)
  n = max(length(a), length(b))
  return DSP.PolynomialRatio(padright(b, n), padright(a, n))
end
getpoles(zpg) = DSP.ZeroPoleGain(zpg).p
getzeros(zpg) = DSP.ZeroPoleGain(zpg).z
getgain(zpg) = DSP.ZeroPoleGain(zpg).k
getnumcoefs(pr) = trimlastzeros!(reverse(DSP.PolynomialRatio(pr).b.coeffs))
getdencoefs(pr) = trimlastzeros!(reverse(DSP.PolynomialRatio(pr).a.coeffs))
function trimlastzeros!(a)
  !iszero(a[end]) && return a
  pop!(a)
  return trimlastzeros!(a)
end
DSP.filt(zpg::DSP.ZeroPoleGain, r...; kwargs...) = filt(polynomialratio(zpg), r...; kwargs...)
function zplane(zs, ps; kwargs...)
	scatter(real.(zs), imag.(zs);
		  marker = (:black, :circle), label="Cero", kwargs...)
	scatter!( real.(ps), imag.(ps);
	  	marker = (:red, :xcross), label="Polo", kwargs...)
end
function zplane(zpg; kwargs...)
	zs = getzeros(zpg)
	ps = getpoles(zpg)
	isempty(zs) && isempty(ps) && (append!(ps, 0))
	
	return zplane(zs, ps; kwargs...)
end
zplane(pr::DSP.PolynomialRatio; kwargs...) = zplane(DSP.ZeroPoleGain(pr); kwargs...)
# Delta
d(n) = n == 0 ? 1. : 0. 
# Escalón
u(n) = n >= 0 ? 1. : 0. 
using Plots
Plots.default(:legend, false)
# Pad vector with zeros on the right until its length is `n`
padright(x, n) = copyto!(zeros(eltype(x), n), x)
"""
Función módulo pero con offset (opcional)
Manda a `t` al intervalo [from, from+length)
sumándole o restándole algún múltiplo de `len`
"""
cshift(t, len, from=0) = mod(t - from, len) + from
# Espectrograma
using IterTools
function stft(x; overlap, window, nfft, rest...)
  nwin = length(window)
  @assert overlap < nwin
  res = [ fft(padright(xseg .* window, nfft)) 
    for xseg in partition(x, nwin, nwin - overlap)]
  
  return [ res[i][j] for j in 1:nfft, i in eachindex(res)]
end
function specgram(x; 
    overlap=0.5, 
    window=hamming(div(length(x), 16)), 
    nfft=length(window), 
    rest...)
  
  window isa Integer && (window = rect(window))
  overlap isa AbstractFloat && (overlap = round(Int, length(window) * overlap))
    
  return stft(x; overlap=overlap, window=window, nfft=nfft)
end
  
specplot(x::AbstractMatrix; kwargs...) = @error "You are entering a Matrix (2D Array). I need a Vector (1D Array)."
function specplot(x::AbstractVector; 
      fs=1, 
      onesided=false, 
      xaxis="Tiempo (s)", 
      yaxis="Frecuencia (Hz)",
      kws...)
    mat = specgram(x; kws...)
  
    fmax = fs
    if onesided
      mat = mat[1:div(size(mat, 1) + 2, 2), :]
      fmax = fs/2
    end
  
  times = range(0; length=size(mat, 2), stop=length(x)/fs) # aprox
  freqs = range(0; length=size(mat, 1), stop=fmax)
  
	# Reubico las frecuencias negativas arriba de todo
  if !onesided
    freqs = cshift.(freqs, fs, -fs/2)
    ord   = sortperm(freqs)
    mat   = mat[ord, :]
    freqs = freqs[ord]
  end
	return heatmap(times, freqs, log.(abs.(mat) .+ eps()); 
          xaxis=xaxis, yaxis=yaxis,
          seriescolor=:bluesreds, legend=true, kws...)
 return times, freqs, mat 
end
function specplot(x :: AbstractVector{<:AbstractFloat}; kws...)
    return specplot(convert.(Complex, x); onesided=true, kws...)
end
37.9s
Julia
specplot (generic function with 3 methods)

Desarrollo

Será evaluado el día de la fecha de entrega. Esta evaluación será mediante preguntas y problemas con la misma modalidad que las evaluaciones parciales. Se deberá aprobar tanto el trabajo entregado como la evaluación. Esta última puede incluir preguntas sobre:

  • Items particulares sobre los ejercicios de esta guía

  • Conceptos teóricos necesarios para realizar los ejercicios

  • DFT

  • Transformada Z

La duración de cada señal para ser considerada un dígito es variable. Cada dígito debe tener una longitud de 70 ms como mínimo, aunque algunos equipos pueden aceptar menor duración de pulso

Utilice la señal dtmf5.wav. La señal contiene un código DTMF (convertido a señal discreta mediante un A/D).

x, sr = wavread_mono( 
dtmf5.wav
);
length(x), sr
1.9s
Julia
#= 
Escribiendo el archivo en la carpeta /results/ lo obtienen como salida.
=#
wavwrite(x, "/results/out.wav"; Fs=sr)
1.2s
Julia

1. Caracterización temporal

Caracterizar y graficar la señal en el tiempo. Escuchar la señal e identificar dentro de la misma la sección donde se realizó el discado. Distinguir las zonas de la señal en donde se están transmitiendo los símbolos y los silencios.

¿Podría considerarse que las señales del código DTMF corresponden a una sección temporal de una señal periódica infinita? Si es así, identifique la frecuencia fundamental de dicha señal.

2. Caracterización espectral

Analizar y graficar el espectro de los distintos símbolos de la señal en forma individual. Comparar los espectros entre sí.

Shift+Enter to run
Julia
Realizar la transformada de Fourier del discado completo. ¿Se puede identificar la secuencia a partir de este espectro?

3. Espectrograma

Realizar la transformada de corto tiempo (espectrograma) de la señal y caracterizarla a partir de la misma. Comprobar y mostrar en gráficos el efecto de distintas ventanas (tipo de ventana y duración) en la visualización del espectrograma.

Shift+Enter to run
Julia

4. Muestreo

¿Cuál es la frecuencia mínima de muestreo requerida para poder trabajar digitalmente con la señal de discado?

Shift+Enter to run
Julia
Sabiendo que las señales discretas se obtuvieron muestreando las originales con inline_formula not implemented, calcular los valores para las frecuencias DTMF presentes en el campo discreto.

Shift+Enter to run
Julia

5. Detección de dígitos

Utilizar el espectrograma y la transformada de Fourier para encontrar la secuencia de dígitos marcada por inspección visual. Mostrar en los gráficos.

Shift+Enter to run
Julia
Mostrar la ventana de menor N con la que se puede todavía discriminar las 2 frecuencias del primer dígito. ¿Cuál es la ventana de mayor N que se puede utilizar? Mostrar los espectrogramas o las DFT para justificar.

Shift+Enter to run
Julia
Uno de los métodos que podría utilizarse para decodificar automáticamente los tonos consiste en un banco de filtros pasabanda centrados en cada una de las frecuencias del sistema DTMF. Diseñe el filtro que corresponde a una de las frecuencias del primer tono de la señal por el método de ventaneo. Justificar la elección del ancho de banda, la longitud, el tipo de ventana.

Shift+Enter to run
Julia
Realizar el diagrama de polos y ceros. Y mostrar cuales son los coeficientes del mismo para filtrar con la función filt.
Shift+Enter to run
Julia

¿Tiene fase lineal?
Runtimes (1)