Accepter et décoder la télévision analogique en utilisant SDR et Python

Salut, Habr.

Aujourd'hui, nous continuons le sujet de la réception SDR et du traitement du signal. Je me suis intéressé à la réception de la télévision analogique tout à fait par accident, après une question d'un des lecteurs. Cependant, cela s'est avéré pas si simple, en raison du manque banal d'échantillons de signal - dans de nombreux endroits, la télévision analogique a déjà été désactivée. Le lecteur a même envoyé un enregistrement avec RTL-SDR, cependant, la largeur de l'enregistrement à RTL est d'environ 2 MHz, tandis que la bande passante du signal TV est d'environ 8 MHz, et rien n'était clair sur l'enregistrement. En conséquence, le sujet a été abandonné pendant longtemps, et enfin, tout à l'heure, lors de mon prochain voyage chez mes proches, j'ai pris SDRPlay avec moi et réglé les fréquences des chaînes de télévision, j'ai vu le signal souhaité sur l'écran.

Un petit programme Python, et tout fonctionne:



Pour ceux qui sont intéressés par les détails, continue sous la coupe.

Théorie


Dans les anciennes années d'après-guerre, lorsque les laboratoires numériques ne connaissaient la transmission du signal numérique que dans des laboratoires secrets, mais que les gens voulaient déjà regarder la télévision, il y avait trois normes analogiques concurrentes. Le premier était l'American NTSC (National Television System System Committee), qui a été développé depuis les années 40, a été «affûté» pour la fréquence du réseau américain de 60 Hz et avait une résolution verticale de seulement 486 lignes. Un peu plus tard en Allemagne, la norme PAL (Phase Alternating Line) a commencé à être développée, qui était légèrement meilleure que la norme américaine (résolution des 576 lignes «entières» et focalisation sur la fréquence du réseau européen de 50 Hz), et un peu plus tard le SECAM français (Séquentiel couleur à mémoire) est apparu. Il a éliminé certaines des lacunes du PAL liées à la reproduction des couleurs, et il existe une version selon laquelle l'adoption de deux normes était également une décision politique, de sorte que les résidents de certains pays ne pouvaient pas regarder les programmes d'autres pays (c'était environ 50 ans avant l'Union européenne unie et Schengen) . D'une manière ou d'une autre, mais le monde entier était divisé comme ceci:



Parce que Habr est toujours un site en langue russe, alors à l'avenir, nous considérerons SECAM, bien que si quelqu'un envoie un échantillon de signal PAL, ce serait également intéressant.

Le spectre SECAM, selon les anciens parchemins , est le suivant:



À gauche, à la fréquence F0, se trouve le signal de luminance modulé en amplitude (L). Il s'agit en fait d'une image en noir et blanc, qui peut toujours être affichée sur un vieux téléviseur noir et blanc chaud et lampe . Le problème de Legacy et la présence d'anciens appareils parmi les utilisateurs existaient déjà à l'époque, de sorte que le canal de couleur a été ajouté séparément, sans perdre la compatibilité avec les anciens téléviseurs. Deux canaux de couleur ont été transmis alternativement en modulation de fréquence à des fréquences de 4,25 et 4,406 MHz. Et enfin, encore plus haut en fréquence, le son était transmis séparément, également en modulation de fréquence.

Soit dit en passant, avec la réception de la télévision à Saint-Pétersbourg, il y a un moment amusant. Comme l'ont rapporté les médias russes, la télévision analogique a été désactivée en octobre:



Cependant, cela ne s'applique qu'aux chaînes d'État ; personne n'oblige les chaînes commerciales à désactiver leurs émissions. Au moins au moment de la rédaction (décembre 2019), environ 5-6 chaînes sont toujours disponibles dans le «analogique» en plein centre de Saint-Pétersbourg. Mais combien de temps cela durera est inconnu, donc ceux qui veulent enregistrer des échantillons de signaux «pour l'histoire» devraient toujours être pressés.

Enfin, il est temps d'activer le SDR et de voir ce que nous avons dans la vie réelle:



Le canal audio n'est pas difficile, vous pouvez simplement le survoler avec la «souris» en HDSDR, sélectionner FM avec une bande passante d'environ 50 KHz et écouter. Nous allons commencer le décodage à partir du canal de luminosité, cela nous permettra d'obtenir une «image» prête à l'emploi.

Décodage


Comme décrit ci-dessus, les signaux de luminance sont transmis à AM. Afin de ne pas écrire le décodeur vous-même, nous utilisons GNU Radio - nous transférons le spectre à une fréquence nulle, démarrons le décodeur AM et enregistrons le résultat dans un fichier.



Maintenant, nous pouvons ouvrir le fichier enregistré en Python:

import numpy as np import matplotlib.pyplot as plt lum_data = np.fromfile("pal_lum.raw", dtype='int32') lum_data = -lum_data - 4700 fs = 9000000//2 x_time = np.linspace(0, len(lum_data)/fs, num=len(lum_data)) plt.plot(x_time, lum_data) 

Nous voyons une séquence de 4 images sur l'écran.



La longueur d'une trame 0,02 s - ce n'est que 1/50 - est un multiple de la fréquence réseau de 50 Hz, dont les signaux servent de «générateur d'horloge» (n'oubliez pas que le signal est analogique). Pour chaque image, 320 lignes sont transmises - nous avons un balayage entrelacé, donc la fréquence d'images finale est de 25 Hz.

Voyons les lignes individuelles plus en détail:



Comme vous pouvez le voir, le début de chaque ligne correspond à une «horloge», puis le swing du signal correspond aux valeurs de luminosité actuelles de cette ligne. Tout est assez simple, et probablement sans pratiquement aucun changement, un tel signal a été appliqué au tube cathodique du téléviseur.

Le reste est une question de technologie. Nous créons une image dans la mémoire et copions deux images dedans, car nous avons entrelacé. La portée du signal ne dépasse pas +200, ce qui nous permet d'écrire ces valeurs directement en couleurs RVB.

 # Output image frame_size = fs*1//50 img_x, img_y = 320, 650 img_size = (img_y, img_x, 3) img_data = np.zeros(img_size, dtype=np.uint8) img_data.fill(255) frame_num = 0 # Frame #1 pos_x, pos_y = 0, 0 for px in range(frame_num*frame_size, (frame_num+1)*frame_size): val = lum_data[px] if val < 0: val = 0 if val > 255: val = 255 img_data[pos_y][pos_x] = (0, val, 0) pos_x += 1 if lum_data[px] <= 0 and lum_data[px+1] > 0: pos_x = 0 pos_y += 2 print("Scan lines 1:", pos_y) # Frame #2 pos_x, pos_y = 0, 0 for px in range((frame_num+1)*frame_size, (frame_num+2)*frame_size): val = lum_data[px] if val < 0: val = 0 if val > 255: val = 255 img_data[pos_y+1][pos_x] = (0, val, 0) pos_x += 1 if lum_data[px] <= 0 and lum_data[px+1] > 0: pos_x = 0 pos_y += 2 img_resized = cv2.resize(img_data, dsize=(3*img_x, img_y), interpolation=cv2.INTER_CUBIC) plt.imshow(img_resized, interpolation='nearest') 

Comme vous pouvez le voir, j'utilise le passage à zéro pour détecter le début d'une nouvelle ligne. L'image s'est avérée compressée verticalement, dans ce cas cela dépend de la fréquence d'échantillonnage du SDR, au final je viens de la redimensionner large.

Le résultat final sur l'animation de 10 images (n'accepte plus l'archive de fichiers Habr):



Conclusion


Il est intéressant d'analyser de telles normes, car d'une part, leur mise en œuvre est assez simple et, d'autre part, leur étude présente également un intérêt historique. Bien sûr, je n'avais pas pour objectif de créer un tuner TV logiciel complet, donc le code est affiché sous une forme minimale.

Si les notes de l'article sont positives, dans la deuxième partie, il sera possible d'envisager de travailler avec la couleur et d'afficher une image couleur à part entière.

Pour ceux qui souhaitent expérimenter par eux-mêmes, le fichier IQ peut être téléchargé ici .

Toutes les expériences réussies.

Source: https://habr.com/ru/post/fr482014/


All Articles