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.
Se divide el sonido del archivo.wav
en partes o ventanas, usando la funcion separaventanas()
.
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)