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)
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...) = "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
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 dtmf8.wav
. La señal contiene un código DTMF (convertido a señal discreta mediante un A/D).
x, sr = wavread_mono(dtmf8.wav);
length(x), sr
#=
Escribiendo el archivo en la carpeta /results/ lo obtienen como salida.
=#
wavwrite(x, "/results/out.wav"; Fs=sr)