Acepta y decodifica TV analógica usando SDR y Python

Hola Habr

Hoy continuamos con el tema de la recepción SDR y el procesamiento de señales. Me interesé por la recepción de la televisión analógica por accidente, luego de una pregunta de uno de los lectores. Sin embargo, esto resultó no ser tan simple, debido a la falta banal de muestras de señal, en muchos lugares la televisión analógica ya se ha apagado. El lector incluso envió una grabación con RTL-SDR, sin embargo, el ancho de la grabación en RTL es de aproximadamente 2 MHz, mientras que el ancho de banda de la señal de TV es de aproximadamente 8 MHz, y no había nada claro en la grabación. Como resultado, el tema fue abandonado por mucho tiempo, y finalmente, justo ahora, en el próximo viaje a mis familiares, llevé SDRPlay conmigo, y sintonizando las frecuencias de los canales de TV, vi la señal deseada en la pantalla.

Un pequeño programa de Python, y todo funciona:



Para aquellos que estén interesados ​​en los detalles, continúe debajo del corte.

Teoría


En los viejos años de la posguerra, cuando las señales secretas digitales solo se conocían en laboratorios secretos, pero la gente ya quería ver televisión, había tres estándares analógicos en competencia. El primero fue el American NTSC (Comité Nacional del Sistema del Sistema de Televisión), que se desarrolló desde los años 40, fue "afilado" para la frecuencia de la red estadounidense de 60Hz y tenía una resolución vertical de solo 486 líneas. Un poco más tarde en Alemania, comenzó a desarrollarse el estándar PAL (Phase Alternating Line), que era ligeramente mejor que el estadounidense (resolución de las líneas "completas" 576 y se centraba en la frecuencia de la red europea de 50Hz), y un poco más tarde apareció el SECAM francés (Séquentiel couleur à mémoire). Eliminó algunas de las deficiencias de la PAL relacionadas con la reproducción del color, y hay una versión de que la adopción de dos estándares también fue una decisión política, por lo que los residentes de algunos países no podían ver programas de otros países (fue aproximadamente 50 años antes de la Unión Europea unida y Schengen) . De una forma u otra, pero el mundo entero estaba dividido así:



Porque Habr todavía es un sitio en idioma ruso, luego en el futuro consideraremos SECAM, aunque si alguien envía una señal de muestra PAL, también sería interesante.

El espectro SECAM, según los antiguos pergaminos , es el siguiente:



A la izquierda, a la frecuencia F0, está la señal de luminancia modulada en amplitud (L). Esta es en realidad una imagen en blanco y negro, que todavía se puede mostrar en un viejo televisor blanco y negro cálido y con lámpara . El problema de Legacy y la presencia de dispositivos antiguos entre los usuarios ya existían entonces, por lo que el canal de color se agregó por separado, sin perder la compatibilidad con los televisores antiguos. Dos canales de color se transmitieron alternativamente en modulación de frecuencia a frecuencias de 4.25 y 4.406 MHz. Y finalmente, aún más alto en frecuencia, el sonido se transmitió por separado, también en modulación de frecuencia.

Por cierto, con la recepción de la televisión en San Petersburgo hay un momento divertido. Como informaron los medios rusos, la televisión analógica se apagó en octubre:



Sin embargo, esto solo se aplica a los canales estatales ; nadie obliga a los canales comerciales a desactivar sus transmisiones. Al menos en el momento de la redacción (diciembre de 2019), aproximadamente 5-6 canales todavía están disponibles en el "análogo" justo en el centro de San Petersburgo. Pero se desconoce cuánto durará, por lo que aquellos que quieran grabar muestras de señal "para el historial" aún deben tener prisa.

Finalmente, es hora de encender el SDR y ver lo que tenemos en la vida real:



El canal de audio no es difícil, simplemente puede pasar el mouse sobre él con el "mouse" en HDSDR, seleccionar FM con un ancho de banda de aproximadamente 50KHz y escuchar. Comenzaremos a decodificar desde el canal de brillo, esto nos permitirá obtener una "imagen" ya hecha.

Decodificación


Como se describió anteriormente, las señales de luminancia se transmiten a AM. Para no escribir un decodificador usted mismo, usamos GNU Radio: transferimos el espectro a frecuencia cero, iniciamos el decodificador AM y guardamos el resultado en un archivo.



Ahora podemos abrir el archivo guardado 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) 

Vemos una secuencia de 4 cuadros en la pantalla.



La longitud de un cuadro de 0.02s - esto es solo 1/50 - es un múltiplo de la frecuencia de red de 50Hz, cuyas señales sirven como un "generador de reloj" (no olvide que la señal es analógica). Para cada cuadro, se transmiten 320 líneas; tenemos un escaneo entrelazado, por lo que la velocidad de cuadro final es de 25 Hz.

Veamos las líneas individuales con más detalle:



Como puede ver, el comienzo de cada línea corresponde a un "reloj", luego la oscilación de la señal corresponde a los valores de brillo actuales en esta línea. Todo es bastante simple, y probablemente sin prácticamente ningún cambio, dicha señal se aplicó al tubo de rayos catódicos del televisor.

El resto es cuestión de tecnología. Creamos una imagen en la memoria y copiamos dos cuadros en ella, porque Nos hemos entrelazado. El alcance de la señal no excede +200, lo que nos permite escribir estos valores directamente como colores RGB.

 # 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') 

Como puede ver, estoy usando el cruce por cero para detectar el inicio de una nueva línea. La imagen resultó estar comprimida verticalmente, en este caso depende de la frecuencia de muestreo del SDR, al final simplemente cambié de tamaño.

El resultado final en la animación de 10 cuadros (ya no acepta el archivo Habr):



Conclusión


Es interesante analizar tales estándares, porque en primer lugar, son bastante simples de implementar y, en segundo lugar, su estudio también es en parte de interés histórico. Por supuesto, no tenía el objetivo de hacer un sintonizador de TV de software completo, por lo que el código se muestra en una forma mínimamente operable.

Si las calificaciones del artículo son positivas, en la segunda parte será posible considerar trabajar con color y mostrar una imagen en color completa.

Para aquellos que desean experimentar por su cuenta, el archivo IQ se puede descargar aquí .

Todos los experimentos exitosos.

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


All Articles