sstv-decoder-prototype/render.py

129 lines
2.8 KiB
Python

#!/usr/bin/env python3
import matplotlib.pyplot as plt
import struct
from PIL import Image
IMG_WIDTH = 640
SYNC_LENGTH = 105
SYNC_THRESH = 100
PORCH_LENGTH = 10
LINE_LENGTH = SYNC_LENGTH + PORCH_LENGTH + 4 * IMG_WIDTH
MAX_DEV = 600
CENTER = 1750
MIN_FREQ = 1200
MAX_FREQ = 2300
COLOR_LOW = 1500
COLOR_HIGH = MAX_FREQ
def to_freq(sample):
freq = CENTER + sample * MAX_DEV
freq = max(MIN_FREQ, freq)
freq = min(MAX_FREQ, freq)
return freq
def to_color(sample):
sample = (sample - 1500) / (COLOR_HIGH - COLOR_LOW)
sample = int(sample * 255)
sample = max(sample, 0)
sample = min(sample, 255)
return sample
def ycbcr_to_rgb(y, cb, cr):
#https://stackoverflow.com/questions/4041840/function-to-convert-ycbcr-to-rgb/15333019#15333019
cr = cr - 128 ;
cb = cb - 128 ;
r = y + 45 * cr / 32 ;
g = y - (11 * cb + 23 * cr) / 32 ;
b = y + 113 * cb / 64 ;
return (int(r), int(g), int(b))
last_sync = False
def is_sync(backlog):
global last_sync
count = 0
for sample in backlog:
if sample < COLOR_LOW:
count += 1
res = False
if count > SYNC_THRESH and not last_sync:
res = True
last_sync = count > SYNC_THRESH
return res
def render_lines(line):
pixels = [[(0,0,0)] * IMG_WIDTH, [(0,0,0)] * IMG_WIDTH]
# Strip porch
porch = len(line) - SYNC_LENGTH - 4 * IMG_WIDTH
if porch < 0:
return pixels
line = line[PORCH_LENGTH:]
for i in range(0, IMG_WIDTH):
y0 = to_color(line[i])
cr = to_color(line[IMG_WIDTH + i])
cb = to_color(line[2 * IMG_WIDTH + i])
y1 = to_color(line[3 * IMG_WIDTH + i])
pixels[0][i] = ycbcr_to_rgb(y0, cb, cr)
pixels[1][i] = ycbcr_to_rgb(y1, cb, cr)
return pixels
def main():
data_file = open("demod.dat", 'rb')
samples = []
syncs = []
pixels = []
backlog = [0.0] * SYNC_LENGTH
line = []
bytes = data_file.read(4)
while len(bytes) == 4:
sample = struct.unpack('f', bytes)[0]
sample = to_freq(sample)
samples += [sample]
backlog = backlog[1:] + [sample]
line += [sample]
if is_sync(backlog) or len(line) > LINE_LENGTH:
if len(line) > SYNC_LENGTH + 4 * IMG_WIDTH:
syncs += [1000]
print("%d \t %d" % (len(line), len(line) - LINE_LENGTH))
pixels += render_lines(line)
else:
print("Short line dropped")
line = []
else:
syncs += [0]
bytes = data_file.read(4)
img = Image.new('RGB', (IMG_WIDTH, len(pixels)))
img_pix = img.load()
for x in range(0, IMG_WIDTH):
for y in range(0, len(pixels)):
img_pix[x,y] = pixels[y][x]
img.save('output.png')
if __name__ == '__main__':
main()