We receive and decode SSTV transmissions from the ISS

Hello, Habr.





As it was written in the recent announcement , from December 24 to December 31, SSTV images are being transmitted from the ISS. The transmission is in the amateur radio band at a frequency of 145.800 MHz and anyone can receive it.





ISS (c) https://en.wikipedia.org/wiki/International_Space_Station
ISS (c) https://en.wikipedia.org/wiki/International_Space_Station

Let's see how it works and how such a signal can be decoded.





General information

" ", - . , SSTV 145.8 . SSTV (Slow-scan television) - . , -3. SSTV , .. . SSTV ( , , ) . - , , - RTL-SDR 35$. , , SSTV .





.. , . - Orbitron, n2yo.com .





, SSTV . 145.800 , , RTL-SDR V3. 35$ , :





, :





, , virtal audio card SDR -.





SSTV, 2-3 . , .





:





SSTV , , PD-120 ( ) 0.5. , 1500 , 2300 .





band-pass , :









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


fs, data = wav.read('HDSDR_20201228_075406Z_145803kHz_AF.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

data1 = butter_bandpass_filter(data, 1400, 2200, fs, order=4)
      
      



. , , :





data_fsk = np.zeros(len(data1))

pos1 = 0
for p in range(0, len(data1)-1):
    if np.sign(data1[p]) != np.sign(data1[p+1]):
        pr = p - pos1
        data_fsk[pos1:p] = np.full(p - pos1, pr)
        pos1 = p
      
      



"" 2D-:





frame_width = int(0.5*fs) + 203
w, h = frame_width, data_fsk.shape[0]//frame_width
image = Image.new('RGB', (w, h))

data_2d = data_fsk[:w*h].reshape((h,w))
for py in range(h):
    for px in range(w):
        lum = int(data_2d[py][px]*16)
        if lum < 0: lum = 0
        if lum > 255: lum = 255
        image.putpixel((px, py), (lum, lum, lum))
      
      



:





plt.imshow(image)
plt.show()
      
      



. 4 . YCrCb, , Y1CrCbY2 - , , . 640480 - 240 , , , 2 .





, 0.5, 240 120, .. 2 . PD-120 .





YCrCb => RGB:





image_rgb = Image.new('RGB', (w//4, 2*h))
for py in range(h):
    for px in range(int(0.125*fs)):
            # PD-120 – 640×480, 190 ?s/pixel
            k = 32
            y0 = 255 - k*data_2d[py][px]
            cr = 255 - k*data_2d[py][px + int(0.1216*fs)]
            cb = 255 - k*data_2d[py][px + 2*int(0.1216*fs)]
            y1 = 255 - k*data_2d[py][px + 3*int(0.1216*fs)]
            image_rgb.putpixel((px, 2*py), (int(y0 + 1.402 * cr), int(y0 - 0.34414 * cb - 0.71414 * cr), int(y0 + 1.772 * cb)))
            image_rgb.putpixel((px, 2*py + 1), (int(y1 + 1.402 * cr), int(y1 - 0.34414 * cb - 0.71414 * cr), int(y1 + 1.772 * cb)))

      
      



, - , :





, SSTV, , :





, . , , 31 ( , ). , Python, SSTV .





ARISS (Amateur Radio on the International Space Station) . , .








All Articles