{
"cells": [
{
"cell_type": "markdown",
"id": "f5dd1112-4bbb-4209-8a00-6b17c82b9660",
"metadata": {},
"source": [
"# G: Control de dispositivos serie (o USB-serie): GPS\n",
"\n",
"Para ejecutar este `notebook` necesitaremos instalar algunos paquetes (y poseer el hardware del que se habla!):\n",
"\n",
"```\n",
" > conda install -c conda-forge tabulate\n",
"\n",
" > conda install -c anaconda pyserial\n",
" \n",
" > conda install -c anaconda pyaudio\n",
"```"
]
},
{
"cell_type": "markdown",
"id": "1e1621a8-538b-4961-9006-d9a979a7c8cb",
"metadata": {},
"source": [
"Usaremos la biblioteca `pySerial` ([documentación](https://pyserial.readthedocs.io/en/latest/pyserial.html)) para leer de un modulo receptor de GPS USB como el de la imagen. Dichos aparatos, que se pueden comprar muy baratos (menos de 10€).\n",
"\n",
"\n",
"\n",
"Un puerto serie se maneja de manera muy similar a como funcionan los archivos normales. Podemos leer de ellos y escribir en ellos.\n",
"\n",
"En un sistema UNIX este tipo de dispositivos crean en el sistema de archivos un fichero con una ruta similar a `/dev/ttyUSBx` o `/dev/ttyACMx` donde `x` puede variar. Por tanto una vez conectado el dispositivo podemos listar los ficheros de dicho directorio para ver si aparece ese puerto:"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "842a67dc-82d7-4cde-9826-9a75c9aae686",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[0m\u001b[40;33;01m/dev/tty\u001b[0m \u001b[40;33;01m/dev/tty23\u001b[0m \u001b[40;33;01m/dev/tty39\u001b[0m \u001b[40;33;01m/dev/tty54\u001b[0m \u001b[40;33;01m/dev/ttyS1\u001b[0m \u001b[40;33;01m/dev/ttyS25\u001b[0m\n",
"\u001b[40;33;01m/dev/tty0\u001b[0m \u001b[40;33;01m/dev/tty24\u001b[0m \u001b[40;33;01m/dev/tty4\u001b[0m \u001b[40;33;01m/dev/tty55\u001b[0m \u001b[40;33;01m/dev/ttyS10\u001b[0m \u001b[40;33;01m/dev/ttyS26\u001b[0m\n",
"\u001b[40;33;01m/dev/tty1\u001b[0m \u001b[40;33;01m/dev/tty25\u001b[0m \u001b[40;33;01m/dev/tty40\u001b[0m \u001b[40;33;01m/dev/tty56\u001b[0m \u001b[40;33;01m/dev/ttyS11\u001b[0m \u001b[40;33;01m/dev/ttyS27\u001b[0m\n",
"\u001b[40;33;01m/dev/tty10\u001b[0m \u001b[40;33;01m/dev/tty26\u001b[0m \u001b[40;33;01m/dev/tty41\u001b[0m \u001b[40;33;01m/dev/tty57\u001b[0m \u001b[40;33;01m/dev/ttyS12\u001b[0m \u001b[40;33;01m/dev/ttyS28\u001b[0m\n",
"\u001b[40;33;01m/dev/tty11\u001b[0m \u001b[40;33;01m/dev/tty27\u001b[0m \u001b[40;33;01m/dev/tty42\u001b[0m \u001b[40;33;01m/dev/tty58\u001b[0m \u001b[40;33;01m/dev/ttyS13\u001b[0m \u001b[40;33;01m/dev/ttyS29\u001b[0m\n",
"\u001b[40;33;01m/dev/tty12\u001b[0m \u001b[40;33;01m/dev/tty28\u001b[0m \u001b[40;33;01m/dev/tty43\u001b[0m \u001b[40;33;01m/dev/tty59\u001b[0m \u001b[40;33;01m/dev/ttyS14\u001b[0m \u001b[40;33;01m/dev/ttyS3\u001b[0m\n",
"\u001b[40;33;01m/dev/tty13\u001b[0m \u001b[40;33;01m/dev/tty29\u001b[0m \u001b[40;33;01m/dev/tty44\u001b[0m \u001b[40;33;01m/dev/tty6\u001b[0m \u001b[40;33;01m/dev/ttyS15\u001b[0m \u001b[40;33;01m/dev/ttyS30\u001b[0m\n",
"\u001b[40;33;01m/dev/tty14\u001b[0m \u001b[40;33;01m/dev/tty3\u001b[0m \u001b[40;33;01m/dev/tty45\u001b[0m \u001b[40;33;01m/dev/tty60\u001b[0m \u001b[40;33;01m/dev/ttyS16\u001b[0m \u001b[40;33;01m/dev/ttyS31\u001b[0m\n",
"\u001b[40;33;01m/dev/tty15\u001b[0m \u001b[40;33;01m/dev/tty30\u001b[0m \u001b[40;33;01m/dev/tty46\u001b[0m \u001b[40;33;01m/dev/tty61\u001b[0m \u001b[40;33;01m/dev/ttyS17\u001b[0m \u001b[40;33;01m/dev/ttyS4\u001b[0m\n",
"\u001b[40;33;01m/dev/tty16\u001b[0m \u001b[40;33;01m/dev/tty31\u001b[0m \u001b[40;33;01m/dev/tty47\u001b[0m \u001b[40;33;01m/dev/tty62\u001b[0m \u001b[40;33;01m/dev/ttyS18\u001b[0m \u001b[40;33;01m/dev/ttyS5\u001b[0m\n",
"\u001b[40;33;01m/dev/tty17\u001b[0m \u001b[40;33;01m/dev/tty32\u001b[0m \u001b[40;33;01m/dev/tty48\u001b[0m \u001b[40;33;01m/dev/tty63\u001b[0m \u001b[40;33;01m/dev/ttyS19\u001b[0m \u001b[40;33;01m/dev/ttyS6\u001b[0m\n",
"\u001b[40;33;01m/dev/tty18\u001b[0m \u001b[40;33;01m/dev/tty33\u001b[0m \u001b[40;33;01m/dev/tty49\u001b[0m \u001b[40;33;01m/dev/tty7\u001b[0m \u001b[40;33;01m/dev/ttyS2\u001b[0m \u001b[40;33;01m/dev/ttyS7\u001b[0m\n",
"\u001b[40;33;01m/dev/tty19\u001b[0m \u001b[40;33;01m/dev/tty34\u001b[0m \u001b[40;33;01m/dev/tty5\u001b[0m \u001b[40;33;01m/dev/tty8\u001b[0m \u001b[40;33;01m/dev/ttyS20\u001b[0m \u001b[40;33;01m/dev/ttyS8\u001b[0m\n",
"\u001b[40;33;01m/dev/tty2\u001b[0m \u001b[40;33;01m/dev/tty35\u001b[0m \u001b[40;33;01m/dev/tty50\u001b[0m \u001b[40;33;01m/dev/tty9\u001b[0m \u001b[40;33;01m/dev/ttyS21\u001b[0m \u001b[40;33;01m/dev/ttyS9\u001b[0m\n",
"\u001b[40;33;01m/dev/tty20\u001b[0m \u001b[40;33;01m/dev/tty36\u001b[0m \u001b[40;33;01m/dev/tty51\u001b[0m \u001b[40;33;01m/dev/ttyACM0\u001b[0m \u001b[40;33;01m/dev/ttyS22\u001b[0m\n",
"\u001b[40;33;01m/dev/tty21\u001b[0m \u001b[40;33;01m/dev/tty37\u001b[0m \u001b[40;33;01m/dev/tty52\u001b[0m \u001b[40;33;01m/dev/ttyprintk\u001b[0m \u001b[40;33;01m/dev/ttyS23\u001b[0m\n",
"\u001b[40;33;01m/dev/tty22\u001b[0m \u001b[40;33;01m/dev/tty38\u001b[0m \u001b[40;33;01m/dev/tty53\u001b[0m \u001b[40;33;01m/dev/ttyS0\u001b[0m \u001b[40;33;01m/dev/ttyS24\u001b[0m\n"
]
}
],
"source": [
"ls /dev/tty*"
]
},
{
"cell_type": "markdown",
"id": "59293fe4-b9a2-4b36-b6b1-983e47fe4235",
"metadata": {},
"source": [
"Estos dispositivos usualmente usan el protocolo [NMEA](https://es.wikipedia.org/wiki/NMEA_0183). Podemos encontrar una descripción más detallada del mismo en [este PDF](https://www.sparkfun.com/datasheets/GPS/NMEA%20Reference%20Manual-Rev2.1-Dec07.pdf).\n",
"\n",
"Lo más básico que podemos hacer es abrir el puerto serie e ir leyendo los mensajes que nos envía el receptor:\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "c453df98-4975-448c-b9f7-d02e46104e11",
"metadata": {},
"outputs": [],
"source": [
"pathSerie = '/dev/ttyACM0'"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "4e128ec9-cba4-4769-bf72-9be5bfd105fe",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"b'$GPRMC,231249.00,V,,,,,,,,,,N*72\\r\\n'\n",
"b'$GPVTG,,,,,,,,,N*30\\r\\n'\n",
"b'$GPGGA,231249.00,,,,,0,00,99.99,,,,,,*69\\r\\n'\n",
"b'$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30\\r\\n'\n",
"b'$GPGSV,2,1,05,02,,,36,03,,,31,06,,,37,11,,,42*7D\\r\\n'\n",
"b'$GPGSV,2,2,05,20,,,33*7E\\r\\n'\n",
"b'$GPGLL,,,,,231249.00,V,N*45\\r\\n'\n",
"b'$GPTXT,01,01,02,u-blox ag - www.u-blox.com*50\\r\\n'\n",
"b'$GPTXT,01,01,02,HW UBX-G70xx 00070000 FF7FFFFFo*69\\r\\n'\n",
"b'$GPTXT,01,01,02,ROM CORE 1.00 (59842) Jun 27 2012 17:43:52*59\\r\\n'\n",
"b'$GPTXT,01,01,02,PROTVER 14.00*1E\\r\\n'\n",
"b'$GPTXT,01,01,02,ANTSUPERV=AC SD PDoS SR*20\\r\\n'\n",
"b'$GPTXT,01,01,02,ANTSTATUS=OK*3B\\r\\n'\n",
"b'$GPTXT,01,01,02,LLC FFFFFFFF-FFFFFFFF-FFFFFFFF-FFFFFFFF-FFFFFFFD*2C\\r\\n'\n",
"b'$GPRMC,231250.00,V,,,,,,,,,,N*7A\\r\\n'\n",
"b'$GPVTG,,,,,,,,,N*30\\r\\n'\n",
"b'$GPGGA,231250.00,,,,,0,00,99.99,,,,,,*61\\r\\n'\n",
"b'$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30\\r\\n'\n",
"b'$GPGSV,2,1,05,02,,,36,03,,,30,06,,,37,11,,,42*7C\\r\\n'\n",
"b'$GPGSV,2,2,05,20,,,34*79\\r\\n'\n",
"b'$GPGLL,,,,,231250.00,V,N*4D\\r\\n'\n",
"b'$GPRMC,231251.00,V,,,,,,,,,,N*7B\\r\\n'\n",
"b'$GPVTG,,,,,,,,,N*30\\r\\n'\n",
"b'$GPGGA,231251.00,,,,,0,00,99.99,,,,,,*60\\r\\n'\n",
"b'$GPGSA,A,1,,,,,,,,,,,,,99.99,99.99,99.99*30\\r\\n'\n",
"b'$GPGSV,2,1,05,02,,,36,03,,,30,06,,,37,11,,,42*7C\\r\\n'\n",
"b'$GPGSV,2,2,05,20,,,34*79\\r\\n'\n",
"b'$GPGLL,,,,,231251.00,V,N*4C\\r\\n'\n",
"Fin ejecución\n"
]
}
],
"source": [
"import serial\n",
"from IPython.display import display, clear_output, HTML\n",
"\n",
"ser = serial.Serial(pathSerie, 9600) # Abrimos el puerto a una velocidad de 9600 baudios (típico en estos dispositivos)\n",
"\n",
"try: \n",
" while True: # Bucle infinito. Seguramente querrás pararlo en algún momento\n",
" data = ser.readline()\n",
"\n",
" if data:\n",
" print(data)\n",
"except KeyboardInterrupt:\n",
" print(\"Fin ejecución\")"
]
},
{
"cell_type": "markdown",
"id": "4d904c55-03a2-464a-a6c2-c21e00166574",
"metadata": {},
"source": [
"Vamos a crear unas pocas clases y funciones para mejorar la salida de información:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "1d39f0d3-1b89-4c63-870b-f527ebb90690",
"metadata": {},
"outputs": [],
"source": [
"import tabulate\n",
"import serial\n",
"from IPython import display\n",
"import time\n",
"\n",
"lastGPRMC = \"\" # Último mensaje de tipo GPRMC codificado como tabla bonita\n",
"lastGPGSV = \"\" # Último mensaje de tipo GPSVC codificado como tabla bonita\n",
"lastDate = \"\" # Última fecha recibida del GPS\n",
"lastTime = \"\" # Última hora recibida del GPS\n",
"\n",
"class printer(str): # Clase auxiliar para imprimir las tablas de datos\n",
" def __repr__(self):\n",
" return self\n",
"\n",
"def parseNMEA(line): # Analiza una línea de mensaje del protocolo NMEA\n",
" if line.startswith(\"$GPRMC\"):\n",
" parseNMEA_RMC(line)\n",
" \n",
" if line.startswith(\"$GPGSV\"):\n",
" parseNMEA_GSV(line)\n",
"\n",
"def parseNMEA_RMC(line): # Analiza una línea NMEA de tipo RMC y almacena su información en una tabla bonita. \n",
" # Tb actualiza la última hora y fecha recibidas \n",
" global lastGPRMC\n",
" global lastDate\n",
" global lastTime\n",
" \n",
" split = line.split(\",\")\n",
" \n",
" tableD = [[\"RMC\", \"UTC Time\", \"Latitude\", \"Longitude\", \"Date\"],\n",
" [\"\", split[1], split[3] + split[4], split[5] + split[6], split[9]]]\n",
" \n",
" table = tabulate.tabulate(tableD, tablefmt='grid')\n",
" \n",
" lastTime = split[1]\n",
" lastDate = split[9]\n",
" \n",
" lastGPRMC = table\n",
" \n",
"def parseNMEA_GSV(line): # Analiza una línea NMEA de tipo GSV y almacena su información en una tabla bonita. \n",
" global lastGPGSV\n",
" \n",
" split = line.split(\",\")\n",
" \n",
" tableD = [\n",
" [\"GSV\", \"Satellites in view\"],\n",
" [\"\", split[3]]\n",
" ]\n",
" \n",
" table = tabulate.tabulate(tableD, tablefmt='grid')\n",
" \n",
" lastGPGSV = table\n",
"\n",
"def imprimeDatos(): # Borra las últimas tablas de datos mostradas y pinta los datos actualizados\n",
" global lastGPRMC\n",
" global lastGPGSV\n",
" \n",
" display.clear_output()\n",
" display.display(printer(lastGPRMC))\n",
" display.display(printer(lastGPGSV))\n",
" \n",
" \n",
"def leeGPS(): # Lee del puerto serie los datos del GPS y los analiza.\n",
" try:\n",
" ser = serial.Serial(pathSerie, 9600)\n",
"\n",
" lastUpdate = time.time() - 1\n",
"\n",
" while True:\n",
" data = ser.readline()\n",
"\n",
" if data:\n",
" data = data.decode('ascii')\n",
" parseNMEA(data)\n",
"\n",
" currentTime = time.time()\n",
"\n",
" if (currentTime - lastUpdate > 0.9): # Si ha pasado más de un segundo desde la última impresión, imprime de nuevo\n",
" imprimeDatos()\n",
"\n",
" lastUpdate = currentTime\n",
" \n",
" except KeyboardInterrupt:\n",
" print(\"Fin ejecución\")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "59c1e55f-6de8-4d4a-86ac-c9bfbbcbca67",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"+-----+-----------+-------------+--------------+--------+\n",
"| RMC | UTC Time | Latitude | Longitude | Date |\n",
"+-----+-----------+-------------+--------------+--------+\n",
"| | 231432.00 | 3709.83273N | 00336.07721W | 290422 |\n",
"+-----+-----------+-------------+--------------+--------+"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"+-----+--------------------+\n",
"| GSV | Satellites in view |\n",
"+-----+--------------------+\n",
"| | 07 |\n",
"+-----+--------------------+"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fin ejecución\n"
]
}
],
"source": [
"leeGPS()"
]
},
{
"cell_type": "markdown",
"id": "1bec812f-7ce5-445f-b0e8-c3fd30ef7c86",
"metadata": {},
"source": [
"## Produciendo sonidos para codificar la fecha y hora\n",
"\n",
"Añadimos unas funciones más para reproducir unos sonidos que codifican la fecha y la hora recibida del GPS estilo \"R2D2\". Solo suena en los minutos \"en punto\" (segundos == 0)."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "b437846c-f525-4a7b-8078-780caa16bb01",
"metadata": {},
"outputs": [],
"source": [
"from IPython.display import Audio\n",
"import numpy as np\n",
"\n",
"notes = np.arange(1, 12) * 440.0\n",
"\n",
"\n",
"def getNote(digito, volume = .1, sampling_rate=44100, duration = 0.1): # Fabrica los samples de una nota determinada (0-10)\n",
" global notes\n",
" \n",
" digito = int(digito)\n",
" \n",
" f = notes[digito]\n",
" samples = volume * (np.sin(2*np.pi*np.arange(sampling_rate*duration)*f/sampling_rate)).astype(np.float32)\n",
" \n",
" return samples\n",
"\n",
"def getNotes(datetime, volume): # Genera los samples de una fecha y hora, cifra a cifra. Empieza por la nota 10 para marcar el comienzo\n",
" samples = getNote(10, volume)\n",
"\n",
" for i in datetime:\n",
" if (type(samples) is np.ndarray):\n",
" samples = np.concatenate((samples, getNote(i, volume)))\n",
"\n",
" samples[-1] = 1.0 # Hack para evitar el que notebook normalice el volumen\n",
" \n",
" return samples\n",
"\n",
"def playDateTime(date, time, volume=0.3): # Reproduce los sonidos para codificar la fecha y hora\n",
" time = time[:6]\n",
" \n",
" datetime = date + time\n",
"\n",
" samples = getNotes(datetime, volume)\n",
" \n",
" display.display(Audio(samples, rate=44100, autoplay=True))\n",
" \n",
"lastSound = 0\n",
"\n",
"def imprimeDatos(): # Sobreescribimos la función que pinta las tablas para que cada minuto en punto suene la codificación de la hora\n",
" global lastGPRMC\n",
" global lastGPGSV\n",
" global lastTime\n",
" global lastDate\n",
" global lastSound\n",
" \n",
" currentTime = time.time()\n",
" \n",
" if currentTime - lastSound > 1.9: # Evitamos que se borre la salida si estan sonando los pitidos\n",
" display.clear_output()\n",
" display.display(printer(lastGPRMC))\n",
" display.display(printer(lastGPGSV))\n",
"\n",
" if lastTime[4:6] == \"00\": # En los minutos en punto pita\n",
" playDateTime(lastDate, lastTime, volume = 1)\n",
" lastSound = currentTime\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "34a288bc-81ca-4594-8e3e-27f03090b4e8",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"+-----+-----------+-------------+--------------+--------+\n",
"| RMC | UTC Time | Latitude | Longitude | Date |\n",
"+-----+-----------+-------------+--------------+--------+\n",
"| | 231443.00 | 3709.83172N | 00336.07816W | 290422 |\n",
"+-----+-----------+-------------+--------------+--------+"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"+-----+--------------------+\n",
"| GSV | Satellites in view |\n",
"+-----+--------------------+\n",
"| | 07 |\n",
"+-----+--------------------+"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fin ejecución\n"
]
}
],
"source": [
"leeGPS()"
]
},
{
"cell_type": "markdown",
"id": "2ce1b851-01f8-4816-835c-ed396cd367c2",
"metadata": {},
"source": [
"Por último vamos a hacer unas rutinas que sean capaces de a partir de un audio en el que haya algunos pitidos decodificarlos y recuperar la fecha y hora en la que fueron emitidos."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "527a766d-69ad-4048-a56f-321042cb1419",
"metadata": {},
"outputs": [],
"source": [
"from numpy.fft import fftfreq, fft\n",
"import matplotlib.pyplot as plt\n",
"\n",
"def buscaNota(notaBuscada, samples, tamanioMuestreo, pasos = 1, desde = 0, hasta = -1): \n",
" '''\n",
" Busca en toda la onda la frecuencia de la nota 10, que es la que estamos usando para señalizar el principio del mensaje\n",
" '''\n",
" \n",
" global notes\n",
" \n",
" if hasta == -1:\n",
" hasta = len(samples)\n",
" \n",
" frecuenciaBuscada=int(notes[notaBuscada]/(44100 / tamanioMuestreo))\n",
"\n",
" maxVal = 0\n",
" maxPos = 0\n",
"\n",
" for pos in range(desde, hasta, pasos): # Vamos analizando trocitos\n",
" samp = samples[pos:pos+tamanioMuestreo]\n",
" \n",
" four = abs(fft(samp))[:int(tamanioMuestreo/2)] # FFT\n",
" \n",
" m = round(four[frecuenciaBuscada], 5) # Redondeamos a 5 cifras decimales la frecuencia que buscamos\n",
"\n",
" if (m > maxVal): # Si es la mayor actualizamos\n",
" maxVal = m\n",
" maxPos = pos\n",
" #print(pos)\n",
" #print(m)\n",
" \n",
" return maxPos\n",
"\n",
"\n",
"\n",
"def queNota(samples): # Analiza un trozo de muestras y devuelve la nota que está más presente de acuerdo a su FFT\n",
" global notes\n",
" \n",
" tamanioMuestreo = len(samples)\n",
" \n",
" four = abs(fft(samples))[:int(len(samples)/2)] # FFT\n",
" \n",
" max = 0\n",
" selectedNote = 0\n",
" \n",
" for i, n in enumerate(notes): # Analizamos las frecuencias de las notas\n",
" frecuenciaBuscada=int(n/(44100 / tamanioMuestreo))\n",
" \n",
" intensity = four[frecuenciaBuscada]\n",
" \n",
" if intensity > max: # Nos quedamos con la más intensa\n",
" max = intensity\n",
" selectedNote = i\n",
"\n",
" return selectedNote\n",
" \n",
"\n",
"def decodifica(samples, tamanioMuestreo): # A partir de una muestra que debe contener todos los digitos del\n",
" # mensaje los decodificamos\n",
" mensaje = \"\"\n",
" \n",
" for pos in range(0, len(samples), tamanioMuestreo): \n",
" ss = samples[pos:pos + tamanioMuestreo]\n",
" nota = queNota(ss)\n",
" \n",
" if (nota < 10):\n",
" mensaje += str(nota)\n",
" \n",
" return mensaje\n",
" \n",
" \n",
" \n",
"def localizaDecodifica(samples): # En la muestra completa de sonido buscamos donde empieza el mensaje\n",
" # y lo decodificamos\n",
" \n",
" notaBuscada = 10 # La nota inicial del mensaje es la 10\n",
" \n",
" tamanioMuestreo = int(44100 * 0.1)\n",
" \n",
" newPos = buscaNota(notaBuscada, samples, int(tamanioMuestreo / 2), pasos = int(tamanioMuestreo/2)) # Buscamos rapidamente la zona donde está\n",
" \n",
" #print(newPos)\n",
" \n",
" newPos2 = buscaNota(notaBuscada, samples, tamanioMuestreo, pasos = 1, desde = newPos - tamanioMuestreo, hasta = newPos + tamanioMuestreo*2) # Hacemos una busqueda fina cerca de la anterior posición\n",
"\n",
" #print(newPos2)\n",
" \n",
" samp = samples[newPos2:newPos2 + tamanioMuestreo*13] # Recortamos el trozo que debe tener el mensaje completo\n",
" \n",
" mensaje = decodifica(samp, tamanioMuestreo) # Decodificamos\n",
" \n",
" return mensaje"
]
},
{
"cell_type": "markdown",
"id": "4dd60396-657d-42ca-8ac3-07151a2ea439",
"metadata": {},
"source": [
"Aquí probamos las rutinas anteriores generando un posible mensaje artificial:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "9d39d1bb-5053-4975-9f41-360e070dc496",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Mensaje localizado y decodificado: 012345678901\n"
]
}
],
"source": [
"ruidoInicial = np.random.rand(67570) * 0.1 # Producimos un ruido blanco inicial\n",
"samples = getNote(10) # Producimos la nota de inicio \n",
"ruidoFinal = np.random.rand(143233) * 0.1 # Ruido blanco final\n",
"\n",
"samples = np.concatenate((ruidoInicial, samples))\n",
"\n",
"for i in range(0, 12): # Producimos doce notas: 012345678901\n",
" s = getNote(i%10)\n",
" \n",
" samples = np.concatenate((samples, s))\n",
"\n",
"samples = np.concatenate((samples, ruidoFinal))\n",
"\n",
"\n",
"samples[-1] = 1 # Pequeño hack porque el plugin de emitir audio normaliza el volumen\n",
"\n",
"display.display(Audio(samples, rate=44100, autoplay=True)) # Reproducimos el mensaje\n",
"\n",
"mensaje = localizaDecodifica(samples)\n",
"\n",
"print(f\"Mensaje localizado y decodificado: {mensaje}\")"
]
},
{
"cell_type": "markdown",
"id": "5059a0e7-045d-45c6-a80a-777f5107d20a",
"metadata": {},
"source": [
"Repetimos lo de antes, pero con un mensaje grabado realmente en un fichero `WAV`. Se oye ruido de fondo real y hasta el tick del reloj de la cocina:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "1c72d2c8-4411-4441-b5ef-deeb3b053fc8",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
" \n",
" "
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Mensaje localizado y decodificado: 270422234800\n"
]
}
],
"source": [
"from scipy.io.wavfile import read\n",
"import numpy as np\n",
"from IPython.display import Audio\n",
"from IPython import display\n",
"\n",
"a = read(\"sonidos/sonido.wav\") # Leemos el fichero wav\n",
"data = np.array(a[1],dtype=float) \n",
"\n",
"\n",
"display.display(Audio(data, rate=44100, autoplay=True)) # Lo reproducimos\n",
"\n",
"mensaje = localizaDecodifica(data) # Obtenemos el mensaje\n",
"\n",
"print(f\"Mensaje localizado y decodificado: {mensaje}\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}