Le motif de cet article est, en fait, une triste occasion. Le radiotélescope de renommée mondiale de l'Observatoire Arecibo à Porto Rico s'est effondré et ne peut pas être restauré. Pendant de nombreuses années, il a été le plus grand radiotélescope du monde à pleine ouverture (diamètre 304 m, gamme de fréquences jusqu'à 10 GHz), à l'aide duquel de nombreuses découvertes ont été faites. Ici, sur la photo de Wikipedia, cela fonctionne toujours:

Source: en.wikipedia.org/wiki/Arecibo_Observatory
Mais le texte parle en fait d'un autre événement. En 1974, un message aux civilisations extraterrestres a été envoyé dans l'espace à partir de ce télescope. Quoi et comment y était encodé, détails sous la coupe.
Codage
Pour commencer, il est intéressant de comprendre comment le message a été véhiculé. Comme vous le savez, la taille du message n'était que de 1679 bits (environ 210 octets), et il était transmis à une fréquence de 2,38 GHz avec une puissance de 450 kW. Une modulation de fréquence à un débit de 10 bits / s a été utilisée pour la transmission. Le nombre 1679 n'a pas été choisi par hasard - il est le produit de deux nombres premiers 23 et 73, il n'y a donc qu'une seule façon d'agrandir l'image sous la forme d'un rectangle.
Je n'ai pas trouvé de message radio prêt à l'emploi au format WAV, mais il est sous forme binaire, et en utilisant Python, il est facile de générer du son vous-même. Ceux qui souhaitent écouter ce que les extraterrestres entendront peuvent télécharger et exécuter le code ci-dessous, qui générera un fichier WAV. Du bruit a également été ajouté au message pour plus de crédibilité.
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")
Pour faciliter l'écoute, j'ai augmenté la séparation des fréquences, dans le message d'origine, elle n'était que de 10 Hz. Malheureusement, habr ne permet pas de joindre des fichiers audio; ceux qui le souhaitent peuvent soit générer le fichier eux-mêmes, soit utiliser un lien temporaire .
À propos, le message a été envoyé en 1974. Juste là:

Source: en.wikipedia.org/wiki/Messier_13 Le
magnifique amas d'étoiles M13 dans la constellation d'Hercule, bien connu de tous les amateurs d'astronomie, et accessible à l'observation même avec de petits télescopes. L'amas est situé à 22 mille années-lumière, donc le message continuera pendant longtemps ...
Nous avons compris le codage, imaginons maintenant que nous avons reçu un tel message - voyons comment il peut être décodé.
Décodage
Le principe de la modulation de fréquence lui-même est simple - différentes fréquences correspondent à zéro et à un. Sur le spectre, cela ressemble à ceci:

Il existe différentes façons de décoder FSK, car la méthode la plus simple consiste à filtrer l'une des fréquences:
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()
Le résultat nous convient assez bien:

bien sûr, le signal qui a passé 22 mille ans dans l'espace risque d'être légèrement affaibli, mais pour simplifier on supposera que les extraterrestres ont de bonnes radios
Vous pouvez facilement déterminer la largeur d'un bit de l'image. Nous devons produire les bits sous forme d'image. Car le message a été envoyé à une civilisation extraterrestre - ceux qui, par définition, ne connaissent pas les systèmes de codage "terrestres" - transmettre une image matricielle était la seule décision logique. Dans la constellation Hercule, ils ne savent probablement pas ce qu'est l'ASCII ou, Dieu nous en préserve, Unicode, mais il est très probablement possible d'afficher le raster sur l'écran n'importe où dans la galaxie. Au moins une civilisation capable de recevoir un signal numérique est susceptible d'avoir une sorte de moniteur pour l'afficher.
Nous ne connaissons pas la taille de l'image, mais nous connaissons la taille d'un bit et nous connaissons la taille du message entier. Vous pouvez simplement trier toutes les options possibles, car il n'y en a pas beaucoup:
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)
Pour plus de clarté, l'image a dû être étirée, car 23 pixels de large par rapport aux normes actuelles ne suffisent pas. Le résultat final est bien visible:

Final Cut:

Contrairement aux images sur Wikipedia , l'image originale est bien sûr monochrome, il n'y a pas de codage couleur dans le signal.
Beaucoup de choses sont encodées dans l'image (conventionnellement, bien sûr), par exemple, une ligne verticale de 2 pixels de large au-dessus de la tête d'une personne est une spirale d'ADN (après tout, c'est évident, n'est-ce pas?). Le décodage des pictogrammes restants peut être trouvé sur le lien ci-dessus sur Wikipedia.
Conclusion
Comme vous pouvez le voir, de nombreuses informations peuvent être encodées sur 210 octets. En général, la tâche d'envoyer un signal dans l'espace lointain est loin d'être simple, car on ne peut qu'espérer les méthodes de modulation les plus simples. Le message parviendra-t-il au destinataire? Bien sûr, probablement peu probable. Je ne sais pas si l'énergie d'une telle "ligne de communication" et la sensibilité approximative du récepteur nécessaire pour recevoir un signal ont été évaluées lors de son envoi. Oui, ce n'est en fait pas si important - si de telles actions incitent quelqu'un à explorer l'espace, ce n'est pas en vain. Eh bien, nous pourrons obtenir la réponse exacte dans 44 mille ans, et j'essaierai de mettre à jour le texte au fur et à mesure que de nouvelles données seront disponibles;)