Sigma-Delta Codificador con Python

Referencia: Leon-Couch, 3–8 Modulación Delta, p.192; Delta-sigma_modulation, Wikipedia ; Sigma-Delta – Modulación

La señal de entrada es el archivo de sonido:

440Hz_44100Hz_16bit_05sec.wav

que es tipo monofónica. La modulación delta-sigma se aplica usando los parámetros:

  • ΔY = deltaY = 0.3 * max(sonido)
  • rango de observación [0, 0.002] segundos para la gráfica
  • muestras en el rango de observación: k

Nota: En el caso de sonido estéreo, con dimensión kx2, se usa solo un canal (sonido[:,0])

Hay que tomar en cuenta que para graficar, la misma se satura con un número grande de muestras . Para escuchar el sonido el tiempo de ‘termina’ será de al menos 1 segundo.
codifica Delta Sigma

Algoritmo en Python

A continuación se describe el algoritmo para codificar en Python, que el el bloque de ingreso especifica el rango de observación en segundos.

% matplotlib inline
# Modulacion Delta Sigma - Codificador
# entrada x(t), salida: y[n]
# propuesta:edelros@espol.edu.ec
import numpy as np
import matplotlib.pyplot as plt
import scipy.io.wavfile as waves

# INGRESO 
# archivo = input('archivo de sonido:' )
archivo = '440Hz_44100Hz_16bit_05sec.wav'
muestreo, sonido = waves.read(archivo)

# rango de observación en segundos
inicia = 0
termina = 0.002
canal = 0   #Usar un canal en caso de estereo
c:\python34\lib\site-packages\scipy\io\wavfile.py:179: WavFileWarning: Chunk (non-data) not understood, skipping it. WavFileWarning)

Si el archivo.wav contiene etiquetas, el proceso de lectura emite una advertencia (WavFileWarning) que no afecta al proceso de codificación.

Se extrae solo una porción del sonido que contiene las k muestras en el intervalo de observación y se envía graficar.

La señal xdigital y ysalida se calcula con las diferencias entre el valor de cada muestra analógica y el valor xdigital anterior; se toma en cuenta solo el signo para acumular y generar el vector ysalida.

# PROCEDIMIENTO
# Codificar Sigma-Delta
deltaY = 0.1*np.max(sonido) 
deltaT = 1/muestreo 

# Usar un canal en caso de estereo
canales=sonido.shape
cuantos=len(canales)
canal = 0   
if (cuantos==1): # Monofónico
    uncanal=sonido[:]  
if (cuantos==2): # Estéreo
    uncanal=sonido[:,canal] 

# Extrae solo una porcion del sonido
donde = int(inicia/deltaT)
# tiempo muestreo de la señal analógica
t = np.arange(inicia,termina,deltaT) 
k = len(t) 

muestra = np.copy(uncanal[donde:donde+k])

# Señal Digital
xdigital = np.zeros(k, dtype=float) 
ysalida = np.zeros(k, dtype=int) 

for i in range(1,k):
    diferencia = muestra[i]-xdigital[i-1]
    if (diferencia>0):
        signo = 1
    else:
        signo = -1
    xdigital[i] = xdigital[i-1]+signo*deltaY
    ysalida[i] = signo
    
parametros=np.array([deltaT,deltaY,k])

Los resultados de pueden observar de dos maneras, en los archivos.txt de parámetros y datos, o en una gráfica.

# SALIDA
print('parámetros:[deltaT, deltaY, k]')
print(parametros)
print('datos:')
print(ysalida)
np.savetxt('deltasigma_parametros.txt',parametros)
np.savetxt('deltasigma_datos.txt',ysalida,fmt='%i')
print('... archivos.txt guardados ...')
parámetros:[deltaT, deltaY, k]
[  2.26757370e-05   6.80100000e+03   8.90000000e+01]
datos:
[ 0  1 -1  1 -1  1  1 -1  1 -1  1  1 -1  1 -1  1 -1  1  1 -1  1 -1  1 -1  1 -1  1 -1  1 -1  1 -1  1 -1 -1  1 -1  1 -1  1 -1 -1  1 -1  1 -1 -1  1 -1  1 -1 -1  1 -1  1 -1 -1  1 -1  1 -1 -1  1 -1  1 -1  1 -1 -1  1 -1  1 -1  1 -1  1 -1  1 -1  1 -1  1 -1  1  1 -1  1 -1  1]
... archivos.txt guardados ...

Gráficas

para graficar y no saturar el gráfico, se recomienda observar solo una porción de los datos determinada por ‘verdesde’ y ‘verhasta’ muestras desde [0,k-1]

verdesde=0
verhasta=90

# Graficar
plt.figure(1)       # define la grafica
plt.suptitle('Codificador Delta-Sigma')

plt.subplot(211)    # grafica de 2x1 y subgrafica 1
plt.ylabel('x(t), x[n]')
plt.plot(t[verdesde:verhasta],muestra[verdesde:verhasta], 'g')
plt.step(t[verdesde:verhasta],xdigital[verdesde:verhasta], where='post',color='m') # Puntos x[n]

plt.subplot(212)    # grafica de 2x1 y subgrafica 2
plt.ylabel('y[n]')
#plt.plot(ysalida, 'bo')     # Puntos y[n]
plt.axis((verdesde,verhasta,-1.1,1.1))
puntos=np.arange(verdesde,verhasta,1)     #posicion eje x para escalon
plt.step(puntos[verdesde:verhasta],ysalida[verdesde:verhasta], where='post')

plt.show()

codifica Delta Sigma
Archivos Resultantes: deltasigma_datos.txt, deltasigma_parametros.txt

4. Morse Decodificador de sonido

Morse – Decodificador de un mensaje Morse desde el sonido en archivo.wav

Para analizar el sonido de un mensaje Morse, se procede de forma semejante al decodificador de un tono morse, revisando la duración de cada símbolo: punto o espacio tienen duración de una marca, la raya tiene una duración de 3 marcas, los simbolos se separan por una pausa con un intervalo de una marca.

telegrafomarca

Se divide el sonido del archivo.wav en partes o ventanas, usando la funcion separaventanas().

ventanas de sonido

Cada ventana se analiza con la transformada de Fourier para encontrar la frecuencia del sonido mas fuerte o marcas de puntos '.', '-', ó ' '. El resultado de análisis de las ventanas es un vector con la marca de frecuencia más fuerte para cada una de las ventanas. (función marcasdeventanas())

La frecuencia o ‘tono frecuente’ se usa como una referencia para diferenciar si existe una señal para un punto o raya Morse. El tono frecuente se obtiene mediante la «moda» en el vector marcas usando scipy.stats.itemfreq() y usando la fila donde donde está la mayor cuenta usando np.argmax().

Para facilitar el análisis de un tono en una marca, se realiza una conversión a una secuencia binaria, usando '1' ó ,'0' si se encuentra el ‘tono frecuente’ en cada ventana.

Para el ejemplo, las ventanas se convierten al vector:
[1 1 1 1 1 1 0 0]

Al contar la duracion de cada intevalo, se determina si el sonido corresponde a un punto, una raya o una espacio:

Ejemplo:
. ... .--. --- .-..

Algoritmo en python

El sonido se obtiene de un archivo.wav del generador de tonos para un punto '.' o raya '-', del cual se leen los datos de sonido, y frecuencia de muestreo.

Para el ejemplo, el sonido del mensaje morse: morsetonoESPOL.wav

Las funciones para cada uno de los procesos descritos se encuentran a continuación

# Código Morse -  Determina un mensaje desde sonido.wav
# propuesta: edelros@espol.edu.ec
import numpy as np
import matplotlib.pyplot as plt
import scipy.io.wavfile as waves
import scipy.fftpack as fourier
import scipy.stats as stats

def separaventanas(sonido, muestreo, duracion = 0.04, partes = 8 ):
    # Divide el sonido en partes o ventanas para estudio
    # duracion = 0.04   # segundos de un punto
    # partes = 8        # divisiones de un punto
    # Extraer ventanas para espectro por partes
    tventana = duracion/partes
    mventana = int(muestreo * tventana) # muestras de una ventana

    # Ajuste de muestras de ventanas para matriz
    anchosonido  = int(len(sonido)/mventana)*mventana 
    sonidoajuste = np.resize(sonido,anchosonido)
    ventanas = np.reshape(sonidoajuste,(-1,mventana))  
    # -1 indica que calcule las filas
    return(ventanas)

def marcasdeventanas(ventanas, muestreo):
    # Analiza con FFT todas las ventanas del sonido
    filas, columnas = np.shape(ventanas)
    marcas = np.zeros(filas,dtype=int)

    # Espectro de Fourier de cada ventana
    # frecuencias para eje frq = fourier.fftfreq(mventana, 1/muestreo) 
    frq = fourier.fftfreq(columnas, 1/muestreo)  
    for f in range(0,filas,1):
        xf = fourier.fft(ventanas[f])
        xf = np.abs(xf)        # magnitud de xf
        tono = np.argmax(xf) # tono, frecuencia mayor 
        marcas[f] = frq[tono]
    return(marcas)

def secuenciabinaria(marcas): 
    # Busca el tono frecuente mayor que cero
    tonosventana = stats.itemfreq(marcas)
    donde = np.argmax(tonosventana[1:,1])
    tonopunto = tonosventana[donde+1,0]

    # Convierte los tonos a secuencia binaria
    secuencia = ''
    for valor in marcas:
        if (valor==tonopunto):
            secuencia = secuencia + '1'
        else:
            secuencia = secuencia + '0'
    return(secuencia)

def duracionmarcas(secuencia):
    # Duración de cada marca
    conteo = []
    caracter = '1'
    if (len(secuencia)>0):
        caracter = secuencia[0]
    i = 0
    k = 0
    while (i<len(secuencia)):
        if (secuencia[i]==caracter):
            k = k + 1
        else:
            conteo.append([int(caracter),k])
            if (caracter=='1'):
                caracter = '0'
            else:
                caracter = '1'
            k = 1
        i = i+1
    conteo = np.array(conteo)
    return(conteo)

def marcasmorse(conteo):
    # Determina la base de un tono
    veces = stats.itemfreq(conteo[:,1])
    donde = np.argmax(veces[:,1])
    base  = veces[donde,0]

    # Genera el código Morse
    tolera = 0.2 # Tolerancia en relacion
    bajo   = 1-tolera
    alto   = 1+tolera
    morse  = ''
    for j in range(0,len(conteo)):
        relacion = conteo[j,1]/base
        simbolo  = conteo[j,0]
        if (simbolo==1):
            if (relacion>(1*bajo) and relacion<(1*alto)):
                morse = morse + '.'
            if  (relacion>(3*bajo) and relacion<(3*alto)):
                morse = morse + '-'
        if  simbolo==0 :
            if (relacion>(3*bajo) and relacion<(3*alto)):
                morse = morse + ' '
            if (relacion>(7*bajo) and relacion<(7*alto)):
                morse=morse+'   '
    return(morse)

# INGRESO 
# archivo = input('nombre del archivo: ')
archivo = 'morsetonoESPOL.wav'
muestreo, sonido = waves.read(archivo)

# PROCEDIMIENTO

ventanas  = separaventanas(sonido, muestreo)
marcas    = marcasdeventanas(ventanas, muestreo)
secuencia = secuenciabinaria(marcas)
conteo    = duracionmarcas(secuencia)
morse     = marcasmorse(conteo)

# SALIDA
print('codigo en morse: ')
print(morse)
codigo en morse: 
. ... .--. --- .-..   .. -- .--. ..- .-.. ... .- -. -.. ---   .-.. .-   ... --- -.-. .. . -.. .- -..   -.. . .-..   -.-. --- -. --- -.-. .. -- .. . -. - ---

Revisión de valores en el procedimiento

Se presentan algunos de los valores intermedios para observar el proceso de detección de símbolos Morse.

# Salida /Observación intermedia
print('ventanas analizadas: ',len(marcas))
print('marcas: ', marcas)
ventanas analizadas:  3287
marcas:  [400 400 400 ...,   0   0   0]

Para facilitar el proceso de detección morse se convierten las marcas a una secuencia binaria, usando como referencia la frecuencia del tono de un punto determinada en la sección anterior.

# Salida /Observación intermedia
if len(secuencia)<100:
    print(secuencia)
else:
    print(secuencia[0:200] + ' ... ')
11111111000000000000000000000000111111110000000011111111000000001111111110000000000000000000000011111111000000001111111111111111111111111000000011111111111111111111111110000000111111111000000000000000 ... 

Cambiar la secuencia a simbolos morse, consiste en determinar la relación entre las veces que aparecen los ‘1’s y ‘0’s, el resultado se puede guardar en un arreglo.

La base de cuántas marcas corresponden a un punto de estima como la «moda» de las veces en que se repite un uno o un cero.

print('simbolo, cuenta:')
print(conteo)
simbolo, cuenta:
[[ 1  8]
 [ 0 24]
 [ 1  8]
 [ 0  8]
 [ 1  8]
 [ 0  8]
 [ 1  9]
 [ 0 23]
 [ 1  8]
 [ 0  8]
 [ 1 25]
 [ 0  7]
 [ 1 25]
...
# Salida
print('codigo en morse: ')
print(morse)
codigo en morse: 
. ... .--. --- .-..   .. -- .--. ..- .-.. ... .- -. -.. ---   .-.. .-   ... --- -.-. .. . -.. .- -..   -.. . .-..   -.-. --- -. --- -.-. .. -- .. . -. - ---

Con el resultado puede usar el decodificador morse de los temas anteriores.

Referencia: Código Morse Wikipedia, Recommendation ITU-R M.1677-1 (10/2009) International Morse code, Leon-Couch, 5–9 Señalización Pasabanda Modulada Binaria (OOK)

3. Morse Decodificador de untono

Referencia: Código Morse Wikipedia, Recommendation ITU-R M.1677-1 (10/2009) International Morse code, Leon-Couch, 5–9 Señalización Pasabanda Modulada Binaria (OOK)

Morse – Decodificador de sonido de Un simbolo

Analisis de archivo.wav en python

Para analizar el sonido de un símbolo Morse, se inicia por recordar la duración de cada símbolo:

Para la detección de un tono, se revisa la frecuencia activa en varios intervalos de tiempo o ventanas.

telegrafomarca

Al contar el número de intevalos (marcas) que aparece un tono vs. el número de intervalos de pausa, se podrá determinar si el sonido corresponde a un punto, una raya o un espacio. Es el proceso contrario a lo realizado para crear el tono, contanto las marcas, la relacion entre marcas y pausas indicará el símbolo recibido.

Algoritmo en python

El sonido para el análisis se obtiene de un archivo.wav, creado con el generador de tonos para un punto '.' o raya '-'. La lectura del archivo proporciona los datos de sonido, y frecuencia de muestreo.

Para el análisis, se supondrá que la ventana tiene la mitad de la duración de un punto, para el ejercicio es 0.04 segundos. Así, una ventana tiene la mitad de muestras que una marca '.' o pausa, obteniendo al menos dos ventanas por cada punto o pausa y tres ventanas por cada raya. Se cuentan las ventanas que tienen sonido o pausa y se comparan para determinar el símbolo.

Que existan al menos dos ventanas por cada punto tienen relación con el muestreo de Nyquist.

Lo expuesto se prueba primero analizando solo una ventana:

# Código Morse -  Determina un simbolo a partir de UN sonido
# propuesta: edelros@espol.edu.ec
import numpy as np
import matplotlib.pyplot as plt
import scipy.io.wavfile as waves
import scipy.fftpack as fourier

# INGRESO 
# archivo = input('nombre del archivo: ')
# archivo = 'morsetonopunto.wav'
archivo = 'morsetonoraya.wav'
muestreo, sonido = waves.read(archivo)

# PROCEDIMIENTO
duracion = 0.04   # segundos de un punto
partes   = 2        # divisiones de un punto

# Extraer ventanas para espectro por partes
tventana = duracion/partes
mventana = int(muestreo * tventana) # muestras de una ventana
# Observacion intermedia
unaventana = sonido[0:mventana]

# SALIDA  GRAFICA
print('muestreo:', muestreo)
print('muestras en sonido', len(sonido))
print('intervalo de ventana: ', tventana)
print('muestras por ventana: ', mventana)

plt.figure(1)
plt.subplot(211)
plt.plot(sonido, label='sonido')
plt.xlabel('muestras')
plt.ylabel('sonido y ventanas')
k = 1
for i in range(0,int(len(sonido)/mventana)):
    plt.axvline(x=k*mventana, color='r', linestyle='--')
    k = k + 1
plt.subplot(212)
plt.plot(unaventana)
plt.xlabel('muestras')
plt.ylabel('unaventana')
plt.margins(0)
plt.show()
muestreo: 11025
muestras en sonido 1764
intervalo de ventana: 0.02
muestras por ventana: 220

Análisis de una ventana

Para determinar si existe un tono en una ventana, se analiza la existencia de una señal observada en el dominio de la frecuencia.

En el dominio de la frecuencia se requiere usar la «Transformada de Fourier» de la señal en la ventana. La Transformada rápida de Fourirer fft() se encuentra disponible en las librerias «scipy» y el rango de frecuencias para la gráfica se obtiene con fftfreq().

En el resultado se busca la frecuencia (np.argmax()) con mayor magnitud (np.abs()) y se determina si corresponde al «tono» morse esperado.

# Espectro de Fourier de una ventana [0:nventana]
xf = fourier.fft(unaventana)
xf = np.abs(xf)  # magnitud de xf
tono = np.argmax(xf)  # tono en donde

# frecuencias para eje
frq = fourier.fftfreq(mventana, 1/muestreo ) 

# SALIDA  GRAFICA /Observacion intermedia
print('frecuencia tono (Hz): ',frq[tono])

plt.figure(2)
plt.xlabel('Frecuencia Hz')
plt.ylabel('Magnitud')
plt.stem(frq,xf)
plt.show()

frecuencia tono (Hz):  451.022727273

Analisis del archivo.wav de un símbolo Morse

Para analizar todo el archivo de sonido, los datos se segmentan por «ventanas» en una matriz, cada fila corresponde a las muestras de una ventana de tiempo.

La matriz debe ser rectangular, por lo que es necesario confirmar que el número de muestras por ventana sean iguales. En caso de no ser así, se realiza un ajuste del ancho del sonido para que el numero de muestras en el archivo sea múltiplo del número de muestras por ventana.

Observar el resultado de crear varias ventanas de tiempo, es semejante a tener un cuaderno en el que cada hoja tiene una imagen con los valores de la señal en cada ventana.

# Ajuste de muestras de ventanas para matriz
antes = len(sonido)
anchosonido = int(len(sonido)/mventana)*mventana 
sonido   = np.resize(sonido,anchosonido)
ventanas = np.reshape(sonido,(-1,mventana))  
# -1 indica que calcule las filas

# SALIDA
print('muestras en sonido: ', antes)
print('muestras recortadas a: ', anchosonido)
print('ventanas: ')
print(ventanas)

muestras en sonido:  1764
muestras recortadas a:  1760
ventanas: 
[[     0   6504  12602 ..., -22161 -24942 -26163]
 [-25748 -23722 -20212 ...,   9594   3241  -3315]
 [ -9663 -15408 -20188 ...,  25762  26158  24919]
 ..., 
 [-15377  -9629  -3278 ...,  -9733 -15468 -20236]
 [-23738 -25755 -26161 ...,      0      0      0]
 [     0      0      0 ...,      0      0      0]]

Para observar el resultado se usa una gráfica 3d de superficie.

# Observacion intermedia
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm

nfilas,ncolumnas = np.shape(ventanas)
x = np.arange(0,ncolumnas) # valores para cada eje
y = np.arange(0,nfilas)
X, Y = np.meshgrid(x, y) # valores para punto.
Z = ventanas             # Matriz

# Salida, gráfico 3d
fig  = plt.figure(3,figsize=plt.figaspect(0.5))
ax   = fig.add_subplot(1, 2, 1, projection='3d')
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                       cmap=cm.coolwarm, linewidth=0,
                       antialiased=False)
plt.xlabel('muestras')
plt.ylabel('ventana')
plt.show()

El proceso de analisis se repite para cada ventana (una fila) en la matriz, guardando cada resultado en un vector «marcas«.

# Analiza con FFT todas las ventanas del sonido
filas, columnas = np.shape(ventanas)
marcas = np.zeros(filas,dtype=float)

# Espectro de Fourier de cada ventana
# frecuencias para eje
frq = fourier.fftfreq(mventana, 1/muestreo)  
for fila in range(0,filas,1):
    xf = fourier.fft(ventanas[fila])
    xf = np.abs(xf)        # magnitud de xf
    tono = np.argmax(xf)   # tono, frecuencia mayor 
    marcas[fila] = frq[tono]

Observamos el resultado en una gráfica 2D.

# SALIDA  GRAFICA  /Observacion intermedia
print(marcas)
plt.figure(4)
plt.stem(marcas)
plt.xlabel('número de marca')
plt.ylabel('frecuencia del tono mas fuerte.')
plt.show()
[ 451.02272727  451.02272727  451.02272727  451.02272727  451.02272727  451.02272727    0.            0.        ]

Para determinar si es punto '.', raya'-' o espacio ' ', se cuentan los noceros y los ceros del arreglo de marcas, la relación entre ellos determina el símbolo que representa el sonido del archivo.wav.

# determina el simbolo
noceros = np.count_nonzero(marcas)
ceros  = len(marcas)-noceros
relacion = int(noceros/ceros)
if (relacion==3):
    simbolo = '-'
if (relacion==1):
    simbolo = '.'
if (relacion==0):
    simbolo = 'espacio'

# SALIDA
print('relacion noceros/ceros:', relacion)
print('simbolo morse detectado: ', simbolo)
relacion noceros/ceros: 3
simbolo morse detectado:  -

Nota: Observe que cualquier frecuencia en una ventana marca un punto '.'. El detalle es tratado en «Morse – Decodificador del sonido de un mensaje» en : # Busca frecuencia del tono punto.


Programa resumido

El resultado del programa resumido se muestra a continuación:

# Código Morse -  Determina un simbolo a partir de UN sonido
# propuesta: edelros@espol.edu.ec
import numpy as np
import matplotlib.pyplot as plt
import scipy.io.wavfile as waves
import scipy.fftpack as fourier

# INGRESO 
# archivo = input('nombre del archivo: ')
# archivo = 'morsetonopunto.wav'
archivo = 'morsetonoraya.wav'
muestreo, sonido = waves.read(archivo)

# PROCEDIMIENTO
duracion = 0.04   # segundos de un punto
partes   = 2      # divisiones de un punto

# Extraer ventanas para espectro por partes
tventana = duracion/partes
mventana = int(muestreo * tventana) # muestras de una ventana

# Ajuste de muestras de ventanas para matriz
antes = len(sonido)
anchosonido = int(len(sonido)/mventana)*mventana 
sonido   = np.resize(sonido,anchosonido)
ventanas = np.reshape(sonido,(-1,mventana))  
# -1 indica que calcule las filas

# Analiza con FFT todas las ventanas del sonido
filas, columnas = np.shape(ventanas)
marcas = np.zeros(filas,dtype=float)

# Espectro de Fourier de cada ventana
# frecuencias para eje
frq = fourier.fftfreq(mventana, 1/muestreo)  
for f in range(0,filas,1):
    xf = fourier.fft(ventanas[f])
    xf = np.abs(xf)        # magnitud de xf
    tono = np.argmax(xf) # tono, frecuencia mayor 
    marcas[f] = frq[tono] 

# determina el simbolo
noceros  = np.count_nonzero(marcas)
ceros    = len(marcas)-noceros
relacion = int(noceros/ceros)
if (relacion==3):
    simbolo = '-'
if (relacion==1):
    simbolo = '.'
if (relacion==0):
    simbolo = 'espacio'

# SALIDA
print('relacion de marcas:', relacion)
print('simbolo morse detectado: ', simbolo)
relacion de marcas: 3
simbolo morse detectado:  -

 

2. Morse Generador de Tonos

Un generador de tonos permite «escuchar» la señal morse de forma similar a la tradicional del telégrafo.

telegrafooperadora

Un punto '.' es la referencia o marca para el sonido morse, teniendo que :

Para identificar los tonos que representan puntos, rayas o espacios, se intercala una pausa con la misma duración que una marca.

telegrafomarca

Para el ejemplo, un punto con una duración de 0.1 segundos se expresa con una señal sinusoidad de 440Hz (una marca, nota musical «La») realizado con muestreo de 11025 veces por segundo.

El resultado buscado para un mensaje completo en código Morse es por ejemplo:

  • mensaje = 'ESPOL impulsando la sociedad del conocimiento'
  • traducido = '. ... .--. --- .-.. .. -- .--. ..- .-.. ... .- -. -.. --- .-.. .- ... --- -.-. .. . -.. .- -.. -.. . .-.. -.-. --- -. --- -.-. .. -- .. . -. - --- '
  • sonido del mensaje morse: morsetonoESPOL.wav

Algoritmo en Python

Un tono para un punto '.', una raya '-', o un espacio ' ', puede ser generado con una función en python.

El tono se realiza con los valores muestreados del tono, separado del próximo '.' o un '-', añadiendo una pausa al final.

El resultado se prepara para ser guardado en un archivo.wav, haciendo que los valores tengan el formato dtype='int16' que es necesario para la generación del archivo de sonido.

# Código Morse -  Generador de tonos
# propuesta: edelros@espol.edu.ec
import numpy as np
import scipy.io.wavfile as waves

def morsetono(simbolo, duracion=0.1 , fs=440, muestreo=11025):
    # duracion=0.1   # segundos de '.' ó ' '
    # fs=440         # Hz del tono
    # muestreo=11025 #en .wav:44100,22050,11025

    marca = 2        # un punto y pausa
    if (simbolo=='-'):
        marca = 4    # raya y pausa
        
    tonodura = marca*duracion     # en segundos
    dt = 1/muestreo 
    t  = np.arange(0,tonodura,dt)  # marcas/tiempo
    tono = np.zeros(len(t), dtype='int16') #tono vacio
    
    volumen = 0.8      # rango [0,1)
    amplitud = int((2**15)*volumen)  #wav 16 bits
    w = 2*np.pi*fs     #frecuencia en radianes
    suena = int((marca-1)*duracion*muestreo)
      
    if (simbolo=='.' or simbolo=='-'):
        for i in range(0,suena):
            tono[i] = amplitud*(np.sin(w*t[i]))
    
    return(muestreo, tono)

Para realizar una prueba a la función, se puede generar un tono '.', '-' y escuchar el resultado en un archivo.wav, similar a los mostrados al inicio para cada símbolo.

# Una prueba
simbolo = '.'
muestreo, sonido=morsetono(simbolo)

# Salida # Archivo de audio.wav
waves.write('morsetonopunto.wav', muestreo, sonido)

Para crear los tonos de un mensaje y escucharlos en un archivo.wav, se procesa el mensaje traducido en morse, símbolo a símbolo, para añadir un punto, una raya o un espacio. Los resultados de cada símbolo se acumulan en el vector sonido.

# traducido = input('Escriba el mensaje morse: ')
traducido = '. ... .--. --- .-..   .. -- .--. ..- .-.. ... .- -. -.. ---   .-.. .-   ... --- -.-. .. . -.. .- -..   -.. . .-..   -.-. --- -. --- -.-. .. -- .. . -. - --- '
# archivo = input('nombre del archivo a guardar: ')
archivo = 'morsetonotest.wav'

# PROCEDIMIENTO
muestreo = 11025  # para .wav: 44100, 22050, 11025
duracion = 0.04

# morse en sonido, como lista por tamaño desconocido
sonido=[]
for i in range(0,len(traducido)):
    muestreo, untono = morsetono(traducido[i], duracion)
    for j in range(0,len(untono)):
        sonido.append(untono[j])
# para .wav convierte en arreglo numpy de 'int16' 
sonido = np.asarray(sonido, dtype='int16')

# SALIDA archivo.wav
print('muetreo: ',muestreo)
print('muestras sonido: ', len(sonido))
print('.... archivo: ' + archivo +'  guardado ....')
waves.write(archivo, muestreo, sonido)

Se crea un archivo.wav con la frecuencia de muestreo y el vector sonido, que puede ser ejecutado con un programa como «windows media player».

muetreo:  11025
muestras sonido:  180810
.... archivo: morsetonotest.wav  guardado ....

Referencia: Código Morse Wikipedia, Women in early radio, Recommendation ITU-R M.1677-1 (10/2009) International Morse code, Leon-Couch, 5–9 Señalización Pasabanda Modulada Binaria (OOK)

1. Morse Codificador y decodificador

Referencia: Leon-Couch, 5–9 Señalización Pasabanda Modulada Binaria (OOK), Código Morse Wikipedia , Telégrafo Wikipedia, Recommendation ITU-R M.1677-1 (10/2009) International Morse code,

Dato Histórico – 1844 Samuel F. B. Morse muestra la línea de telégrafo en Baltimore, MD y Washington, DC

telegrafollave

El código Morse fué muy usado en telegrafía, transmisiones por radio marítimas y aéreas.

Conocido también como alfabeto Morse, cambia los caracteres alfanuméricos a códigos morse combinando puntos '.' y rayas '-'. La separación entre códigos morse se realiza con un espacio ' ', mientras que en la separación entre palabras se usan 3 espacios ' '.

Ejemplo:
un mensaje: ESPOL impulsando la sociedad del conocimiento
. ... .--. --- .-..   .. -- .--. ..- .-.. ... .- -. -.. ---   .-.. .-   ... --- -.-. .. . -.. .- -..   -.. . .-..   -.-. --- -. --- -.-. .. -- .. . -. - --- 

Un tema que permite revisar varios conceptos de telecomunicaciones es el telégrafo y el código Morse.

Los bloques de desarrollo usados en ésta sección se presentan el el siguiente diagrama

morsebloques


Codificador en Python

La traducción entre un caracter del alfabeto a un código morse se puede facilitar con una función dedicada a ésta tarea.

Para buscar un equivalente de un caracter en una tabla de conversión a código, por facilidad de usar como índice un caracter se propone usar un diccionario de python.

Algoritmo en Python

# Código Morse -  codificador
# propuesta: edelros@espol.edu.ec

def morsecodec(caracter):
    equivale={
        'A':'.-', 'B':'-...', 'C':'-.-.', 
        'CH':'----', 'D':'-..', 'E':'.', 
        'F':'..-.', 'G':'--.', 'H':'....', 
        'I':'..', 'J':'.---', 'K':'-.-',
        'L':'.-..', 'M':'--', 'N':'-.', 
        'Ñ':'--.--', 'O':'---', 'P':'.--.', 
        'Q':'--.-', 'R':'.-.', 'S':'...', 
        'T':'-', 'U':'..-', 'V':'...-',
        'W':'.--', 'X':'-..-', 'Y':'-.--', 
        'Z':'--..',
        '0':'-----', '1':'.----', '2':'..---', 
        '3':'...--', '4':'....-', '5':'.....', 
        '6':'-....', '7':'--...', '8':'---..', 
        '9':'----.', 
        '.':'.-.-.-', ',':'-.-.--', '?':'..--..', 
        '"':'.-..-.', '!':'--..--', ' ':' '}
    caracter=caracter.upper()
    codigo=equivale[caracter]
    return(codigo)

El programa para cambiar un mensaje a su versión morse se simplifica usando la función anterior.

# INGRESO
mensaje = input('un mensaje: ')
# mensaje= 'ESPOL impulsando la sociedad del conocimiento'

# PROCEDIMIENTO
n = len(mensaje)
traducido = ''
for caracter in mensaje:
    traducido = traducido + morsecodec(caracter) + ' '

# SALIDA
print(traducido)

La ejecución del programa y la función tiene el resultado mostrado en el ejemplo.

un mensaje: ESPOL impulsando la sociedad del conocimiento
. ... .--. --- .-..   .. -- .--. ..- .-.. ... .- -. -.. ---   .-.. .-   ... --- -.-. .. . -.. .- -..   -.. . .-..   -.-. --- -. --- -.-. .. -- .. . -. - --- 

Decodificador en Python

Para realizar el proceso inverso al codificador, se ingresa un código morse obtenido de un receptor a un decodificador.

Ejemplo:

mensaje en morse:
. ... .--. --- .-.. .. -- .--. ..- .-.. ... .- -. -.. --- .-.. .- ... --- -.-. .. . -.. .- -.. -.. . .-.. -.-. --- -. --- -.-. .. -- .. . -. - ---
ESPOL IMPULSANDO LA SOCIEDAD DEL CONOCIMIENTO

telegraforeceptor
telégrafo receptor

Decodificar Morse requiere usar una tabla de equivalentes descrita en el estándar internacional (ITU), en el orden contrario al codificador.

En un mensaje morse, las palabras son separadas por tres espacios ' '; en una palabra, cada código se separa por un espacio ' '.

Algoritmo en Python

La traducción de un codigo morse a un caracter del alfabeto, se puede realizar con una función.
De forma semejante al codificador, se usa la tabla de equivalentes como un diccionario.

# Código Morse -  DECOdificador
# propuesta: edelros@espol.edu.ec

def morsedeco(codigo): 
    equivale={ 
        '.-':'A', '-...':'B', '-.-.':'C', 
        '----':'CH', '-..':'D', '.':'E', 
        '..-.':'F', '--.':'G', '....':'H', 
        '..':'I', '.---':'J', '-.-':'K',
        '.-..':'L', '--':'M', '-.':'N', 
        '--.--':'Ñ', '---':'O', '.--.':'P', 
        '--.-':'Q', '.-.':'R', '...':'S', 
        '-':'T', '..-':'U', '...-':'V',
        '.--':'W', '-..-':'X', '-.--':'Y', 
        '--..':'Z',
        '-----':'0', '.----':'1', '..---':'2', 
        '...--':'3', '....-':'4', '.....':'5', 
        '-....':'6', '--...':'7', '---..':'8', 
        '----.':'9', 
        '.-.-.-':'.', '-.-.--':',', '..--..':'?', 
        '.-..-.':'"', '--..--':'!', '   ':' ', 
        ' ':' '}
    caracter=equivale[codigo]
    return(caracter)

El programa para cambiar un mensaje morse al alfabeto se simplifica usando la función anterior.

La ejecución del programa y la función tiene el resultado mostrado en el ejemplo.

# INGRESO
traducido = input('mensaje en morse: ')
# traducido='. ... .--. --- .-..   .. -- .--. ..- .-.. ... .- -. -.. ---   .-.. .-   ... --- -.-. .. . -.. .- -..   -.. . .-..   -.-. --- -. --- -.-. .. -- .. . -. - --- '
    
# PROCEDIMIENTO
mensaje  = ''
palabras = traducido.split('   ')
for unapalabra in palabras:
    letras = unapalabra.split(' ')
    for unaletra in letras:
        mensaje = mensaje+morsedeco(unaletra)
    mensaje = mensaje + ' '

# SALIDA
print('mensaje decodificado: ')
print(mensaje)

mensaje en morse:

. ... .--. --- .-.. .. -- .--. ..- .-.. ... .- -. -.. --- .-.. .- ... --- -.-. .. . -.. .- -.. -.. . .-.. -.-. --- -. --- -.-. .. -- .. . -. - ---

mensaje decodificado:
ESPOL IMPULSANDO LA SOCIEDAD DEL CONOCIMIENTO