Codificamos y decodificamos un mensaje para civilizaciones extraterrestres

Hola Habr.



El motivo de este artículo es, de hecho, una triste ocasión. El radiotelescopio mundialmente famoso del Observatorio de Arecibo en Puerto Rico se derrumbó y no se puede restaurar. Durante muchos años fue el radiotelescopio más grande del mundo con una apertura completa (diámetro 304 m, rango de frecuencia de hasta 10 GHz), con la ayuda del cual se hicieron muchos descubrimientos. Aquí en la foto de Wikipedia, todavía está funcionando:





Fuente: en.wikipedia.org/wiki/Arecibo_Observatory



Pero el texto es en realidad sobre otro evento. En 1974, este telescopio envió un mensaje a las civilizaciones extraterrestres al espacio. Qué y cómo estaba codificado en él, detalles debajo del corte.



Codificación



Para empezar, es interesante comprender cómo se transmitió el mensaje. Como saben, el tamaño del mensaje era de solo 1679 bits (aproximadamente 210 bytes) y se transmitía a una frecuencia de 2,38 GHz con una potencia de 450 kW. Para la transmisión se utilizó modulación de frecuencia a una velocidad de 10 bit / s. El número 1679 no fue elegido por casualidad, es el producto de dos números primos 23 y 73, por lo que solo hay una forma de expandir la imagen en forma de rectángulo.



No pude encontrar un mensaje de radio listo para usar en formato WAV, pero está en forma binaria, y usando Python es fácil generar sonido por su cuenta. Aquellos que deseen escuchar lo que escucharán los extraterrestres pueden descargar y ejecutar el código a continuación, que generará un archivo WAV. También se ha agregado ruido al mensaje para mayor credibilidad.



generate.py
import scipy.io.wavfile as wav
import scipy.signal as signal
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import os


message = """0000001010101000000000000101000001010000000100100010001000100
             1011001010101010101010100100100000000000000000000000000000000
             0000011000000000000000000011010000000000000000000110100000000
             0000000000101010000000000000000001111100000000000000000000000
             0000000001100001110001100001100010000000000000110010000110100
             0110001100001101011111011111011111011111000000000000000000000
             0000010000000000000000010000000000000000000000000000100000000
             0000000001111110000000000000111110000000000000000000000011000
             0110000111000110001000000010000000001000011010000110001110011
             0101111101111101111101111100000000000000000000000000100000011
             0000000001000000000001100000000000000010000011000000000011111
             1000001100000011111000000000011000000000000010000000010000000
             0100000100000011000000010000000110000110000001000000000011000
             1000011000000000000000110011000000000000011000100001100000000
             0110000110000001000000010000001000000001000001000000011000000
             0010001000000001100000000100010000000001000000010000010000000
             1000000010000000100000000000011000000000110000000011000000000
             1000111010110000000000010000000100000000000000100000111110000
             0000000010000101110100101101100000010011100100111111101110000
             1110000011011100000000010100000111011001000000101000001111110
             0100000010100000110000001000001101100000000000000000000000000
             0000000001110000010000000000000011101010001010101010100111000
             0000001010101000000000000000010100000000000000111110000000000
             0000001111111110000000000001110000000111000000000110000000000
             0110000000110100000000010110000011001100000001100110000100010
             1000001010001000010001001000100100010000000010001010001000000
             0000001000010000100000000000010000000001000000000000001001010
             00000000001111001111101001111000"""

def fftnoise(f):
    f = np.array(f, dtype='complex')
    n_p = (len(f) - 1) // 2
    phases = np.random.rand(n_p) * 2 * np.pi
    phases = np.cos(phases) + 1j * np.sin(phases)
    f[1:n_p+1] *= phases
    f[-1:-1-n_p:-1] = np.conj(f[1:n_p+1])
    return np.fft.ifft(f).real

def band_limited_noise(min_freq, max_freq, samples, samplerate=1):
    freqs = np.abs(np.fft.fftfreq(samples, 1/samplerate))
    f = np.zeros(samples)
    idx = np.where(np.logical_and(freqs>=min_freq, freqs<=max_freq))[0]
    f[idx] = 1
    return fftnoise(f)


message = ''.join(i for i in message if i.isdigit())
print("Original message:")
print(message)
print()

# Generate message
fs = 11025
f1, f2 = 3000, 4000
t_sym = 0.1
data = np.zeros(int(fs * t_sym * len(message)))
for p in range(len(message)):
    samples = np.linspace(0, t_sym, int(fs * t_sym), endpoint=False)
    freq = f2 if message[p] == '1' else f1
    data[int(fs * t_sym)*p:int(fs * t_sym)*(p + 1)] = 10000*(0.25*np.sin(2 * np.pi * freq * samples) + band_limited_noise(50, 5000, len(samples), fs))

wav.write('arecibo.wav', fs, np.int16(data))
print("WAV file saved")
      
      





Para la comodidad de escuchar, aumenté la separación de frecuencias, en el mensaje original era solo de 10 Hz. Desafortunadamente, habr no permite adjuntar archivos de sonido; aquellos que lo deseen pueden generar el archivo ellos mismos o usar un enlace temporal .



Por cierto, el mensaje se envió en 1974. Allí mismo:





Fuente: en.wikipedia.org/wiki/Messier_13 El



hermoso cúmulo de estrellas M13 en la constelación de Hércules, bien conocido por todos los amantes de la astronomía, y accesible para la observación incluso con telescopios pequeños. El cúmulo está ubicado a 22 mil años luz de distancia, por lo que el mensaje continuará durante mucho tiempo ...



Descubrimos la codificación, ahora imagina que recibimos un mensaje así, veamos cómo se puede decodificar.



Descodificación



El principio de la modulación de frecuencia en sí es simple: diferentes frecuencias corresponden a cero y uno. En el espectro, se ve algo como esto:







hay diferentes formas de decodificar FSK, como el método más simple, solo filtra una de las frecuencias:



fs, data = wav.read('arecibo.wav')

def butter_bandpass(lowcut, highcut, fs, order=5):
    nyq = 0.5 * fs
    low = lowcut / nyq
    high = highcut / nyq
    b, a = signal.butter(order, [low, high], btype='band')
    return b, a

def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):
    b, a = butter_bandpass(lowcut, highcut, fs, order=order)
    y = signal.lfilter(b, a, data)
    return y

f1, f2 = 3000, 4000
data_f2 = butter_bandpass_filter(data, f2 - 200, f2 + 200, fs, order=3)

plt.plot(data)
plt.plot(data_f2)
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.title("Signal")
plt.show()

      
      





El resultado nos sienta bastante bien: por







supuesto, la señal que ha pasado 22 mil años en el espacio es probable que se debilite ligeramente , pero por simplicidad asumiremos que los extraterrestres tienen radios buenas y no chinas ...



Puede determinar fácilmente el ancho de un bit a partir de la imagen. Necesitamos generar los bits como una imagen. Porque el mensaje fue enviado a una civilización extraterrestre - aquellos que, por definición, no conocen los sistemas de codificación "terrestre" - transmitir una imagen raster fue la única decisión lógica. En la constelación de Hércules, lo más probable es que no sepan qué es ASCII o, Dios no lo quiera, Unicode, pero lo más probable es que se muestre la trama en la pantalla en cualquier lugar de la galaxia. Es probable que al menos una civilización capaz de recibir una señal digital tenga algún tipo de monitor para mostrarla.



No sabemos el tamaño de la imagen, pero sabemos el tamaño de un bit y sabemos el tamaño de todo el mensaje. Simplemente puede ordenar todas las opciones posibles, ya que no hay tantas:



ss = 1102  # Width of one symbol in samples
for iw in range(12*ss, 25*ss, ss):
    w, h = iw, 80
    image = Image.new('RGB', (w, h))

    px, py = 0, 0
    for p in range(data_f2.shape[0]):
        image.putpixel((px, py), (0, int(data_f2[p]//32), 0))
        px += 1
        if px >= w:
            px = 0
            py += 1
            if py >= h:
                break

    image = image.resize((w//10, 100*h))
    image.save("1/image-%d.png" % iw)
      
      







Para mayor claridad, la imagen tuvo que ser estirada, porque 23 píxeles de ancho para los estándares actuales no es, por decirlo suavemente, suficiente. El resultado final es bastante visible:







Final Cut: a







diferencia de las imágenes de Wikipedia , la imagen original es, por supuesto, monocromática, no hay codificación de colores en la señal.



Muchas cosas están codificadas en la imagen (convencionalmente, por supuesto), por ejemplo, una línea vertical de 2 píxeles de ancho por encima de la cabeza de una persona es una espiral de ADN (después de todo, es obvio, ¿no?). La decodificación de los pictogramas restantes se puede ver en el enlace de arriba en Wikipedia.



Conclusión



Como puede ver, se puede codificar una gran cantidad de información en 210 bytes. En general, la tarea de enviar una señal al espacio profundo está lejos de ser simple, porque uno solo puede esperar los métodos de modulación más simples. ¿Llegará el mensaje al destinatario? Por supuesto, lo más probable es que sea poco probable. No sé si se evaluó la energía de tal "línea de comunicación" y la sensibilidad aproximada del receptor requerida para recibir una señal al enviarla. Sí, esto en realidad no es tan importante: si tales acciones inspiran a alguien a explorar el espacio, entonces no fue en vano. Bueno, podremos obtener la respuesta exacta en 44 mil años, e intentaré actualizar el texto a medida que haya nuevos datos disponibles;)



All Articles