Add implementation of analog TV signal generator (PAL so far)

Quick and dirty Howto:
make && tv/osmotv --sdr-soapy --sdr-tx-gain 60  -r 15000000 -c 21 tx-fubk --sdr-tune-args "OFFSET=-3000000"
pull/1/head
Andreas Eversberg 6 years ago
parent a52c89800c
commit 496aff5a79
  1. 2
      .gitignore
  2. 39
      configure.ac
  3. 2
      src/Makefile.am
  4. 11
      src/common/Makefile.am
  5. 388
      src/common/img.c
  6. 7
      src/common/img.h
  7. 40
      src/tv/Makefile.am
  8. 284
      src/tv/bas.c
  9. 25
      src/tv/bas.h
  10. 94
      src/tv/channels.c
  11. 4
      src/tv/channels.h
  12. 877
      src/tv/font.c
  13. 3
      src/tv/font.h
  14. 511
      src/tv/fubk.c
  15. 3
      src/tv/fubk.h
  16. 67
      src/tv/image.c
  17. 3
      src/tv/image.h
  18. 492
      src/tv/main.c
  19. 35
      src/tv/tv_modulate.c
  20. 3
      src/tv/tv_modulate.h
  21. 183
      src/tv/vcr.c
  22. 3
      src/tv/vcr.h

2
.gitignore vendored

@ -22,6 +22,7 @@ compile
m4
src/common/libcommon.a
src/common/libmobile.a
src/common/libimage.a
src/anetz/anetz
src/bnetz/bnetz
src/cnetz/cnetz
@ -31,6 +32,7 @@ src/amps/amps
src/tacs/tacs
src/jtacs/jtacs
src/r2000/radiocom2000
src/tv/osmotv
sim/cnetz_sim
src/test/test_filter
src/test/test_compandor

@ -29,6 +29,39 @@ AC_CHECK_LIB([pthread], [main])
PKG_CHECK_MODULES(ALSA, alsa >= 1.0)
# disabled due to problems with api compatibilty with imagemagick
#AC_ARG_ENABLE(graphicsmagick,
# [AS_HELP_STRING(
# [--disable-graphicsmagick],
# [Disable building graphicsmagick]
# )],
# [enable_graphicsmagick=$enableval], [enable_graphicsmagick="yes"])
#if test x"$enable_graphicsmagick" = x"yes"
#then
# PKG_CHECK_MODULES(GRAPHICSMAGICK, GraphicsMagick >= 1.3.16, , enable_graphicsmagick=no)
#fi
#if test x"$enable_graphicsmagick" = x"yes"
#then
# somethingmagick=yes
#fi
AC_ARG_ENABLE(imagemagick,
[AS_HELP_STRING(
[--disable-imagemagick],
[Disable building imagemagick]
)],
[enable_imagemagick=$enableval], [enable_imagemagick="yes"])
if test x"$enable_imagemagick" = x"yes"
then
PKG_CHECK_MODULES(IMAGEMAGICK, ImageMagick >= 6.0.0, , enable_imagemagick=no)
fi
if test x"$enable_imagemagick" = x"yes"
then
somethingmagick=yes
fi
AM_CONDITIONAL(ENABLE_MAGICK, test x"$somethingmagick" = x"yes")
with_sdr=no
AC_ARG_WITH([uhd], [AS_HELP_STRING([--with-uhd], [compile with UHD driver @<:@default=check@:>@]) ], [], [with_uhd="check"])
AC_ARG_WITH([soapy], [AS_HELP_STRING([--with-soapy], [compile with SoapySDR driver @<:@default=check@:>@]) ], [], [with_soapy="check"])
@ -37,8 +70,9 @@ AS_IF([test "x$with_soapy" != xno], [PKG_CHECK_MODULES(SOAPY, SoapySDR >= 0.6.0,
AM_CONDITIONAL(HAVE_UHD, test "x$with_uhd" == "xyes" )
AM_CONDITIONAL(HAVE_SOAPY, test "x$with_soapy" == "xyes" )
AM_CONDITIONAL(HAVE_SDR, test "x$with_sdr" == "xyes" )
AS_IF([test "x$with_uhd" == "xyes"],[AC_MSG_NOTICE( Compiling with UHD SDR support )], [AC_MSG_NOTICE( UHD SDR not support )])
AS_IF([test "x$with_soapy" == "xyes"],[AC_MSG_NOTICE( Compiling with SoapySDR support )], [AC_MSG_NOTICE( SoapySDR not support )])
AS_IF([test "x$with_uhd" == "xyes"],[AC_MSG_NOTICE( Compiling with UHD SDR support )], [AC_MSG_NOTICE( UHD SDR not supported )])
AS_IF([test "x$with_soapy" == "xyes"],[AC_MSG_NOTICE( Compiling with SoapySDR support )], [AC_MSG_NOTICE( SoapySDR not supported )])
AS_IF([test "x$somethingmagick" == "xyes"],[AC_MSG_NOTICE( Compiling with ImageMagick )],[AC_MSG_NOTICE( ImageMagick not supported )])
AC_OUTPUT(
src/common/Makefile
@ -50,6 +84,7 @@ AC_OUTPUT(
src/tacs/Makefile
src/jtacs/Makefile
src/r2000/Makefile
src/tv/Makefile
src/test/Makefile
src/Makefile
sim/Makefile

@ -1,3 +1,3 @@
AUTOMAKE_OPTIONS = foreign
SUBDIRS = common anetz bnetz cnetz nmt amps tacs jtacs r2000 test
SUBDIRS = common anetz bnetz cnetz nmt amps tacs jtacs r2000 tv test

@ -1,6 +1,6 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) $(UHD_CFLAGS) $(SOAPY_CFLAGS)
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) $(GRAPHICSMAGICK_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(UHD_CFLAGS) $(SOAPY_CFLAGS)
noinst_LIBRARIES = libcommon.a libmobile.a
noinst_LIBRARIES = libcommon.a libmobile.a libimage.a
libcommon_a_SOURCES = \
sample.c \
@ -31,6 +31,9 @@ libmobile_a_SOURCES = \
display_status.c \
main_mobile.c
libimage_a_SOURCES = \
img.c
if HAVE_SDR
AM_CPPFLAGS += -DHAVE_SDR
@ -55,3 +58,7 @@ libcommon_a_SOURCES += \
soapy.c
endif
if ENABLE_MAGICK
AM_CPPFLAGS += -DWITH_MAGICK
endif

@ -0,0 +1,388 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "img.h"
int save_depth = 16;
#ifdef WITH_MAGICK
#include <magick/api.h>
/* load given image to memory. return short RGB values */
unsigned short *load_img(int *width, int *height, const char *filename, int index)
{
Image *image = NULL;
ImageInfo *imageinfo = NULL;
ExceptionInfo exception;
unsigned short *img = NULL;
MagickCoreGenesis(NULL, MagickFalse);
// InitializeMagick(NULL);
imageinfo = CloneImageInfo(0);
GetExceptionInfo(&exception);
sprintf(imageinfo->filename, filename, index);
image = ReadImage(imageinfo, &exception);
if (!image) {
// printf("failed to read image '%s' via *magick\n", filename);
goto exit;
}
*width = image->columns;
*height = image->rows;
img = (unsigned short *)malloc((*width) * (*height) * 3 * 2);
if (!img) {
printf("%s:failed to allocate image data\n", __func__);
goto exit;
}
ExportImagePixels(image, 0, 0, *width, *height, "RGB", ShortPixel, img, NULL);
// DispatchImage(image, 0, 0, *width, *height, "RGB", ShortPixel, img, NULL);
exit:
if (image)
DestroyImage(image);
if (imageinfo)
DestroyImageInfo(imageinfo);
MagickCoreTerminus();
// DestroyMagick();
return img;
}
/* save given image */
int save_img(unsigned short *img, int width, int height, int alpha, const char *filename, int index)
{
int rc = -1;
Image *image = NULL;
ImageInfo *imageinfo = NULL;
ExceptionInfo exception;
MagickCoreGenesis(NULL, MagickFalse);
// InitializeMagick(NULL);
imageinfo = CloneImageInfo(0);
GetExceptionInfo(&exception);
imageinfo->quality = 100;
if (strlen(filename) >= 4 && !strcmp(filename + strlen(filename) - 4, ".png"))
imageinfo->quality = 1;
image=ConstituteImage(width, height, (alpha)?"RGBA":"RGB", ShortPixel, img, &exception);
if (!image) {
printf("%s:failed to prepare to write image\n", __func__);
goto exit;
}
/* store as 16 bit, if lib and format supports it */
image->depth = save_depth;
sprintf(image->filename, filename, index); /* ACHTUNG: nicht imageinfo!!! */
if (!WriteImage(imageinfo, image)) {
printf("%s:failed to write image\n", __func__);
goto exit;
}
rc = 0;
exit:
if (image)
DestroyImage(image);
if (imageinfo)
DestroyImageInfo(imageinfo);
MagickCoreTerminus();
// DestroyMagick();
return rc;
}
#else
/* load given image to memory. return short RGB values */
unsigned short *load_img(int *width, int *height, const char *filename, int index)
{
FILE *fp = NULL;
unsigned short *img = NULL;
char line[256];
int words, i;
sprintf(line, filename, index);
// printf("reading image: %s\n", line);
fp = fopen(line, "r");
if (!fp) {
// printf("failed to read ppm image '%s'\n", filename);
goto exit;
}
again1:
if (!fgets(line, sizeof(line), fp)) {
printf("%s:failed to read image depth\n", __func__);
goto exit;
}
line[sizeof(line)-1] = '\0';
if (line[0]) line[strlen(line)-1] = '\0';
if (line[0] == '#')
goto again1;
if (!!strcmp(line, "P6")) {
printf("%s:expecting image depth 'P6'\n", __func__);
goto exit;
}
again2:
if (!fgets(line, sizeof(line), fp)) {
printf("%s:failed to read image size\n", __func__);
goto exit;
}
line[sizeof(line)-1] = '\0';
if (line[0]) line[strlen(line)-1] = '\0';
if (line[0] == '#')
goto again2;
sscanf(line, "%d %d", width, height);
// printf("Image size: w=%d h=%d\n", *width, *height);
again3:
if (!fgets(line, sizeof(line), fp)) {
printf("%s:failed to read line '255' or '65535'\n", __func__);
goto exit;
}
line[sizeof(line)-1] = '\0';
if (line[0]) line[strlen(line)-1] = '\0';
if (line[0] == '#')
goto again3;
if (!strcmp(line, "255")) {
words = 1;
} else
if (!strcmp(line, "65535")) {
words = 2;
} else {
printf("%s:expecting line '255' or '65535'\n", __func__);
goto exit;
}
img = (unsigned short *)malloc((*width) * (*height) * 3 * 2);
if (!img) {
printf("%s:failed to allocate image data\n", __func__);
goto exit;
}
if (fread(img, (*width) * (*height) * 3 * words, 1, fp) != 1) {
printf("%s:failed to read image data\n", __func__);
goto exit;
}
/* char to short (255 -> 65535) */
if (words == 1) {
unsigned char *from = (unsigned char *)img, c;
for (i = (*width) * (*height) * 3 - 1; i >= 0; i--) {
c = from[i];
img[i] = (c << 8) | c;
}
} else {
/* correct byte order */
unsigned short v;
unsigned char *from = (unsigned char *)img;
for (i = 0; i < (*width) * (*height) * 3; i++) {
v = ((*from++) << 8);
v |= (*from++);
img[i] = v;
}
}
exit:
if (fp)
fclose(fp);
return img;
}
/* save given image */
int save_img(unsigned short *img, int width, int height, int alpha, const char *filename, int index)
{
FILE *fp = NULL;
int rc = -1;
char line[256];
int i;
unsigned short v;
unsigned char *to;
if (alpha) {
printf("%s:cannot save alpha component with PPM support only\n", __func__);
alpha = 0;
goto exit;
}
sprintf(line, filename, index);
// printf("writing image: %s\n", line);
fp = fopen(line, "w");
if (!fp) {
printf("%s:failed to write image\n", __func__);
goto exit;
}
fprintf(fp, "P6\n%d %d\n65535\n", width, height);
/* correct byte order, write and restore byte order */
to = (unsigned char *)img;
for (i = 0; i < width * height * 3; i++) {
v = img[i];
if (i/100*i == i) { printf("%04x ", v); }
(*to++) = v >> 8;
(*to++) = v;
}
rc = fwrite(img, width * height * 3 * 2, 1, fp);
to = (unsigned char *)img;
for (i = 0; i < width * height * 3; i++) {
v = (*to++) << 8;
v |= (*to++);
img[i] = v;
}
if (rc != 1) {
printf("%s:failed to write image data\n", __func__);
goto exit;
}
rc = 0;
exit:
if (fp)
fclose(fp);
return rc;
}
#endif
int save_img_array(double *array, int width, int height, int alpha, const char *filename, int index)
{
int rc = -1;
unsigned short *img = NULL;
int components;
#ifndef WITH_MAGICK
if (alpha) {
printf("%s:warning, cannot save alpha component with PPM support only\n", __func__);
alpha = 0;
}
#endif
components = (alpha) ? 4 : 3;
img = (unsigned short *)malloc(width * height * components * 2);
if (!img) {
printf("%s:failed to allocate image data\n", __func__);
goto exit;
}
array2img_short(array, width, height, img, width, height, alpha);
save_img(img, width, height, alpha, filename, index);
rc = 0;
exit:
if (img)
free(img);
return rc;
}
/* convert an image to a three dimensional array of double
* the size is: width, height, 3
*/
void img2array_short(unsigned short *img, int iw, int ih, double *array, int aw, int ah)
{
int x, y;
int channel;
double r, g, b;
channel = aw * ah;
for (y = 0; y < ih; y++) {
for (x = 0; x < iw; x++) {
r = img[(x+iw*y)*3] / 65535.0F;
g = img[(x+iw*y)*3+1] / 65535.0F;
b = img[(x+iw*y)*3+2] / 65535.0F;
array[x+aw*y] = r;
array[x+aw*y+channel] = g;
array[x+aw*y+channel+channel] = b;
}
}
}
/* convert a three dimensional array of double to an image
* the size is: width, height, 3
*/
void array2img_short(double *array, int aw, int ah, unsigned short *img, int iw, int ih, int alpha)
{
int x, y, c;
int channel, components;
double r, g, b, a;
channel = aw * ah;
components = (alpha) ? 4 : 3;
for (y = 0; y < ih; y++) {
for (x = 0; x < iw; x++) {
r = array[x+aw*y];
c = (r * 65535.0F + 0.5F);
if (c < 0)
c = 0;
else if (c > 65535)
c = 65535;
img[(x+iw*y)*components] = c;
g = array[x+aw*y+channel];
c = (g * 65535.0F + 0.5F);
if (c < 0)
c = 0;
else if (c > 65535)
c = 65535;
img[(x+iw*y)*components+1] = c;
b = array[x+aw*y+channel+channel];
c = (b * 65535.0F + 0.5F);
if (c < 0)
c = 0;
else if (c > 65535)
c = 65535;
img[(x+iw*y)*components+2] = c;
if (alpha) {
a = array[x+aw*y+channel+channel+channel];
c = (a * 65535.0F + 0.5F);
if (c < 0)
c = 0;
else if (c > 65535)
c = 65535;
img[(x+iw*y)*components+3] = c;
}
}
}
}
/*
* scale down image in img_buffer by calculating average
*/
void scale_img(unsigned short *img, int width, int height, int scale)
{
int w, h, i, j, x, y;
int r, g, b;
if (scale == 1)
return;
w = width / scale;
h = height / scale;
for (i = 0; i < h; i++) {
for (j = 0; j < w; j++) {
r = g = b = 0;
for (y = 0; y < scale; y++) {
for (x = 0; x < scale; x++) {
r += img[((i*scale+y) * width + j*scale+x) * 3 + 0];
g += img[((i*scale+y) * width + j*scale+x) * 3 + 1];
b += img[((i*scale+y) * width + j*scale+x) * 3 + 2];
}
}
img[(i * w + j)*3 + 0] = r / scale / scale;
img[(i * w + j)*3 + 1] = g / scale / scale;
img[(i * w + j)*3 + 2] = b / scale / scale;
}
}
}

@ -0,0 +1,7 @@
extern int save_depth;
unsigned short *load_img(int *width, int *height, const char *filename, int index);
int save_img(unsigned short *img, int width, int height, int alpha, const char *filename, int index);
int save_img_array(double *array, int width, int height, int alpha, const char *filename, int index);
void img2array_short(unsigned short *img, int iw, int ih, double *array, int aw, int ah);
void array2img_short(double *array, int aw, int ah, unsigned short *img, int iw, int ih, int alpha);
void scale_img(unsigned short *img, int width, int height, int scale);

@ -0,0 +1,40 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) -fstack-check
bin_PROGRAMS = \
osmotv
osmotv_SOURCES = \
bas.c \
fubk.c \
font.c \
vcr.c \
image.c \
tv_modulate.c \
channels.c \
main.c
osmotv_LDADD = \
$(COMMON_LA) \
$(top_builddir)/src/common/libimage.a \
$(top_builddir)/src/common/libcommon.a \
$(ALSA_LIBS) \
$(UHD_LIBS) \
$(SOAPY_LIBS) \
$(GRAPHICSMAGICK_LIBS) $(IMAGEMAGICK_LIBS) \
-lm
if HAVE_SDR
AM_CPPFLAGS += -DHAVE_SDR
endif
if HAVE_UHD
AM_CPPFLAGS += -DHAVE_UHD
endif
if HAVE_SOAPY
AM_CPPFLAGS += -DHAVE_SOAPY
endif
if ENABLE_MAGICK
AM_CPPFLAGS += -DWITH_MAGICK
endif

@ -0,0 +1,284 @@
/* generate a BAS signal
*
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <stdint.h>
#include <math.h>
#include "../common/sample.h"
#include "../common/iir_filter.h"
#include "bas.h"
#include "vcr.h"
#include "fubk.h"
#include "image.h"
#define WHITE_LEVEL 1.0
#define BLACK_LEVEL 0.32
#define PORCH_LEVEL 0.3
#define SYNC_LEVEL 0.0
#define H_SYNC_START 0.0000015
#define H_SYNC_STOP 0.0000062
#define H_LINE_START 0.000012
#define H_LINE_END 0.000064
#define H_SYNC2_START (H_SYNC_START + H_LINE_END/2.0)
#define H_SYNC2_STOP (H_SYNC_STOP + H_LINE_END/2.0)
#define V_SYNC_STOP (H_SYNC2_START - (H_SYNC_STOP - H_SYNC_START))
#define V_SYNC2_STOP (H_SYNC_START - (H_SYNC_STOP - H_SYNC_START) + H_LINE_END) // wraps, so we substract H_LINE_END
#define SYNC_RAMP 0.0000003
#define IMAGE_RAMP 0.0000002
#define H_CBURST_START 0.0000068
#define H_CBURST_STOP 0.0000094
#define COLOR_CARRIER 4433618.75
#define COLOR_OFFSET 0.0000004
#define BURST_AMPLITUDE 0.15
#define COLOR_FILTER_ITER 1
void bas_init(bas_t *bas, double samplerate, enum bas_type type, int fbas, double circle_radius, int color_bar, int grid_only, const char *station_id, unsigned short *img, int width, int height)
{
memset(bas, 0, sizeof(*bas));
bas->samplerate = samplerate;
bas->type = type;
bas->fbas = fbas;
bas->v_polarity = 1;
bas->circle_radius = circle_radius;
bas->color_bar = color_bar;
bas->grid_only = grid_only;
bas->station_id = station_id;
bas->img = img;
bas->img_width = width;
bas->img_height = height;
/* filter color signal */
iir_lowpass_init(&bas->lp_u, 1300000.0, samplerate, COLOR_FILTER_ITER);
iir_lowpass_init(&bas->lp_v, 1300000.0, samplerate, COLOR_FILTER_ITER);
/* filter final FBAS, so we prevent from beeing in the audio carrier spectrum */
iir_lowpass_init(&bas->lp_y, 4500000.0, samplerate, COLOR_FILTER_ITER);
}
static inline double ramp(double x)
{
return 0.5 - 0.5 * cos(x * M_PI);
}
int bas_generate(bas_t *bas, sample_t *sample)
{
double step = 1.0 / bas->samplerate;
int total_i = 0, i, c, line, middlefield_line;
double x = 0, render_start, render_end;
int have_image;
sample_t color_u[(int)(bas->samplerate / 15625.0) + 10];
sample_t color_v[(int)(bas->samplerate / 15625.0) + 10];
double _sin, _cos;
double color_step = COLOR_CARRIER / bas->samplerate * 2 * M_PI;
/* the offset is specified by delaying Y signal by 0.4 uS. */
// additianlly we compensate the delay caused by the color filter, that is 2 samples per iteration */
int color_offset = (int)(bas->samplerate * COLOR_OFFSET); // + 2 * COLOR_FILTER_ITER;
for (line = 0; line < 625; line++) {
/* reset color */
memset(color_u, 0, sizeof(color_u));
memset(color_v, 0, sizeof(color_v));
/* render image interlaced */
have_image = 1;
/* switch off to have black image */
#if 1
if (line >= 24-1 && line <= 310-1)
middlefield_line = (line - (24-1)) * 2 + 1;
else if (line >= 336-1 && line <= 622-1)
middlefield_line = (line - (336-1)) * 2;
else
have_image = 0;
if (have_image) {
switch (bas->type) {
case BAS_FUBK:
/* render FUBK test image */
fubk_gen_line(sample, x, bas->samplerate, color_u, color_v, bas->v_polarity, H_LINE_START, H_LINE_END, middlefield_line, bas->circle_radius, bas->color_bar, bas->grid_only, bas->station_id);
break;
case BAS_IMAGE: {
/* 574 lines of image are to be rendered */
int img_line = middlefield_line - (574 - bas->img_height) / 2;
if (img_line >= 0 && img_line < bas->img_height) {
/* render image data */
image_gen_line(sample, x, bas->samplerate, color_u, color_v, bas->v_polarity, H_LINE_START, H_LINE_END, bas->img + bas->img_width * img_line * 3, bas->img_width);
}
}
break;
case BAS_VCR:
/* render VCR test image */
vcr_gen_line(sample, x, bas->samplerate, color_u, color_v, bas->v_polarity, H_LINE_START, H_LINE_END, middlefield_line / 2);
break;
}
}
#endif
i = 0;
/* porch before sync */
render_start = H_SYNC_START - SYNC_RAMP / 2;
while (x < render_start) {
sample[i++] = PORCH_LEVEL;
x += step;
}
/* ramp to sync level */
render_end = render_start + SYNC_RAMP;
while (x < render_end) {
sample[i++] = ramp((x - render_start) / SYNC_RAMP) * (SYNC_LEVEL - PORCH_LEVEL) + PORCH_LEVEL;
x += step;
}
/* sync (long sync for vertical blank) */
if (line <= 3-1 || line == 314-1 || line == 315-1)
render_start = V_SYNC_STOP - SYNC_RAMP / 2;
else
render_start = H_SYNC_STOP - SYNC_RAMP / 2;
while (x < render_start) {
sample[i++] = SYNC_LEVEL;
x += step;
}
/* ramp to porch level */
render_end = render_start + SYNC_RAMP;
while (x < render_end) {
sample[i++] = ramp((x - render_start) / SYNC_RAMP) * (PORCH_LEVEL - SYNC_LEVEL) + SYNC_LEVEL;
x += step;
}
if (have_image) {
/* porch after sync, before color burst */
render_start = H_CBURST_START;
while (x < render_start) {
sample[i++] = PORCH_LEVEL;
x += step;
}
/* porch after sync, color burst */
render_start = H_CBURST_STOP;
while (x < render_start) {
/* shift color burst to the right, it is shifted back when modulating */
color_u[i+color_offset] = -0.5 * BURST_AMPLITUDE; /* - 180 degrees */
color_v[i+color_offset] = 0.5 * BURST_AMPLITUDE * (double)bas->v_polarity; /* +- 90 degrees */
sample[i++] = PORCH_LEVEL;
x += step;
}
/* porch after sync, after color burst */
render_start = H_LINE_START;
while (x < render_start) {
sample[i++] = PORCH_LEVEL;
x += step;
}
/* ramp to image */
render_end = render_start + IMAGE_RAMP;
while (x < render_end) {
/* scale level of image to range of BAS signal */
sample[i] = sample[i] * (WHITE_LEVEL - BLACK_LEVEL) + BLACK_LEVEL;
/* ramp from porch level to image level */
sample[i] = ramp((x - render_start) / IMAGE_RAMP) * (sample[i] - PORCH_LEVEL) + PORCH_LEVEL;
i++;
x += step;
}
/* image */
render_start = H_LINE_END - IMAGE_RAMP;
while (x < render_start) {
/* scale level of image to range of BAS signal */
sample[i] = sample[i] * (WHITE_LEVEL - BLACK_LEVEL) + BLACK_LEVEL;
i++;
x += step;
}
/* ramp to porch level */
render_end = H_LINE_END;
while (x < render_end) {
/* scale level of image to range of BAS signal */
sample[i] = sample[i] * (WHITE_LEVEL - BLACK_LEVEL) + BLACK_LEVEL;
/* ramp from image level to porch level */
sample[i] = ramp((x - render_start) / IMAGE_RAMP) * (PORCH_LEVEL - sample[i]) + sample[i];
i++;
x += step;
}
} else {
/* draw porch to second sync */
if (line <= 5-1 || (line >= 311-1 && line <= 317-1) || line >= 623-1) {
/* porch before sync */
render_start = H_SYNC2_START - SYNC_RAMP / 2;
while (x < render_start) {
sample[i++] = PORCH_LEVEL;
x += step;
}
/* ramp to sync level */
render_end = render_start + SYNC_RAMP;
while (x < render_end) {
sample[i++] = ramp((x - render_start) / SYNC_RAMP) * (SYNC_LEVEL - PORCH_LEVEL) + PORCH_LEVEL;
x += step;
}
/* sync (long sync for vertical blank) */
if (line <= 2-1 || line == 313-1 || line == 314-1 || line == 315-1)
render_start = V_SYNC2_STOP - SYNC_RAMP / 2;
else
render_start = H_SYNC2_STOP - SYNC_RAMP / 2;
while (x < render_start) {
sample[i++] = SYNC_LEVEL;
x += step;
}
/* ramp to porch level */
render_end = render_start + SYNC_RAMP;
while (x < render_end) {
sample[i++] = ramp((x - render_start) / SYNC_RAMP) * (PORCH_LEVEL - SYNC_LEVEL) + SYNC_LEVEL;
x += step;
}
}
/* porch to end of line */
render_end = H_LINE_END;
while (x < render_end) {
sample[i++] = PORCH_LEVEL;
x += step;
}
}
if (bas->fbas) {
/* filter color carrier */
iir_process(&bas->lp_u, color_u, i);
iir_process(&bas->lp_v, color_v, i);
/* modulate color to sample */
bas->color_phase = fmod(bas->color_phase + color_step * (double)color_offset, 2.0 * M_PI);
for (c = color_offset; c < i; c++) {
bas->color_phase += color_step;
if (bas->color_phase >= 2.0 * M_PI)
bas->color_phase -= 2.0 * M_PI;
_sin = sin(bas->color_phase);
_cos = cos(bas->color_phase);
sample[c-color_offset] += color_u[c] * _cos - color_v[c] * _sin;
sample[c-color_offset] += color_u[c] * _sin + color_v[c] * _cos;
// puts(debug_amplitude(sample[c-color_offset]));
}
/* filter bas signal */
iir_process(&bas->lp_y, sample, i);
}
/* flip polarity of V signal */
bas->v_polarity = -bas->v_polarity;
/* increment sample buffer to next line */
sample += i;
/* return x */
x -= H_LINE_END;
/* sum total i */
total_i += i;
}
return total_i;
}

@ -0,0 +1,25 @@
enum bas_type {
BAS_FUBK,
BAS_VCR,
BAS_IMAGE,
};
typedef struct bas {
double samplerate;
enum bas_type type;
int fbas; /* if color shall be added */
double circle_radius; /* radius of circle in grid units */
int color_bar; /* show only color bar on all lines */
int grid_only; /* show only the grid */
const char *station_id; /* text to display as station id */
double color_phase; /* current phase of color carrier */
int v_polarity; /* polarity of V color vector */
unsigned short *img; /* image data, if it should be used */
int img_width, img_height; /* size of image */
iir_filter_t lp_y, lp_u, lp_v; /* low pass filters */
} bas_t;
void bas_init(bas_t *bas, double samplerate, enum bas_type type, int fbas, double circle_radius, int color_bar, int grid_only, const char *station_id, unsigned short *img, int width, int height);
int bas_generate(bas_t *bas, sample_t *sample);

@ -0,0 +1,94 @@
#include <stdio.h>
static struct tv_channels {
int channel;
double video_mhz;
double audio_mhz;
} tv_channels[] = {
{ 1, 41.25, 46.75 },
{ 2, 48.25, 53.75 },
{ 3, 55.25, 60.75 },
{ 4, 62.25, 67.75 },
{ 5, 175.25, 180.75 },
{ 6, 182.25, 187.75 },
{ 7, 189.25, 194.75 },
{ 8, 196.25, 201.75 },
{ 9, 203.25, 208.75 },
{ 10, 210.25, 215.75 },
{ 11, 217.25, 222.75 },
{ 12, 224.25, 229.75 },
{ 21, 471.25, 476.75 },
{ 22, 479.25, 484.75 },
{ 23, 487.25, 492.75 },
{ 24, 495.25, 500.75 },
{ 25, 503.25, 508.75 },
{ 26, 511.25, 516.75 },
{ 27, 519.25, 524.75 },
{ 28, 527.25, 532.75 },
{ 29, 535.25, 540.75 },
{ 30, 543.25, 548.75 },
{ 31, 551.25, 556.75 },
{ 32, 559.25, 564.75 },
{ 33, 567.25, 572.75 },
{ 34, 575.25, 580.75 },
{ 35, 583.25, 588.75 },
{ 36, 591.25, 596.75 },
{ 37, 599.25, 604.75 },
{ 38, 607.25, 612.75 },
{ 39, 615.25, 620.75 },
{ 40, 623.25, 628.75 },
{ 41, 631.25, 636.75 },
{ 42, 639.25, 644.75 },
{ 43, 647.25, 652.75 },
{ 44, 655.25, 660.75 },
{ 45, 663.25, 668.75 },
{ 46, 671.25, 676.75 },
{ 47, 679.25, 684.75 },
{ 48, 687.25, 692.75 },
{ 49, 695.25, 700.75 },
{ 50, 703.25, 708.75 },
{ 51, 711.25, 716.75 },
{ 52, 719.25, 724.75 },
{ 53, 727.25, 732.75 },
{ 54, 735.25, 740.75 },
{ 55, 743.25, 748.75 },
{ 56, 751.25, 756.75 },
{ 57, 759.25, 764.75 },
{ 58, 767.25, 772.75 },
{ 59, 775.25, 780.75 },
{ 60, 783.25, 788.75 },
{ 61, 791.25, 796.75 },
{ 62, 799.25, 804.75 },
{ 63, 807.25, 812.75 },
{ 64, 815.25, 820.75 },
{ 65, 823.25, 828.75 },
{ 66, 831.25, 836.75 },
{ 67, 839.25, 844.75 },
{ 68, 847.25, 852.75 },
{ 69, 855.25, 860.75 },
{ 0, 0, 0, }
};
double get_tv_video_frequency(int channel)
{
int i;
for (i = 0; tv_channels[i].channel; i++) {
if (tv_channels[i].channel == channel)
return tv_channels[i].video_mhz * 1e6;
}
return 0.0;
}
void list_tv_channels(void)
{
int i;
printf("List of TV channels in MHz:\n\n");
printf("Channel Video Audio\n");
printf("------------------------\n");
for (i = 0; tv_channels[i].channel; i++) {
printf("%d\t%.2f\t%.2f\n", tv_channels[i].channel, tv_channels[i].video_mhz, tv_channels[i].audio_mhz);
}
}

@ -0,0 +1,4 @@
double get_tv_video_frequency(int channel);
void list_tv_channels(void);

@ -0,0 +1,877 @@
#include <stdint.h>
#include "font.h"
static const uint8_t font_c64_data[] = {
/* --- new character ' ' (32) */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
/* --- new character '!' (33) */
0x18, /* ...##... */
0x18, /* ...##... */
0x18, /* ...##... */
0x18, /* ...##... */
0x00, /* ........ */
0x00, /* ........ */
0x18, /* ...##... */
0x00, /* ........ */
/* --- new character '"' (34) */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
/* --- new character '#' (35) */
0x66, /* .##..##. */
0x66, /* .##..##. */
0xff, /* ######## */
0x66, /* .##..##. */
0xff, /* ######## */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x00, /* ........ */
/* --- new character '$' (36) */
0x18, /* ...##... */
0x3e, /* ..#####. */
0x60, /* .##..... */
0x3c, /* ..####.. */
0x06, /* .....##. */
0x7c, /* .#####.. */
0x18, /* ...##... */
0x00, /* ........ */
/* --- new character '%' (37) */
0x62, /* .##...#. */
0x66, /* .##..##. */
0x0c, /* ....##.. */
0x18, /* ...##... */
0x30, /* ..##.... */
0x66, /* .##..##. */
0x46, /* .#...##. */
0x00, /* ........ */
/* --- new character '&' (38) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x38, /* ..###... */
0x67, /* .##..### */
0x66, /* .##..##. */
0x3f, /* ..###### */
0x00, /* ........ */
/* --- new character ''' (39) */
0x06, /* .....##. */
0x0c, /* ....##.. */
0x18, /* ...##... */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
/* --- new character '(' (40) */
0x0c, /* ....##.. */
0x18, /* ...##... */
0x30, /* ..##.... */
0x30, /* ..##.... */
0x30, /* ..##.... */
0x18, /* ...##... */
0x0c, /* ....##.. */
0x00, /* ........ */
/* --- new character ')' (41) */
0x30, /* ..##.... */
0x18, /* ...##... */
0x0c, /* ....##.. */
0x0c, /* ....##.. */
0x0c, /* ....##.. */
0x18, /* ...##... */
0x30, /* ..##.... */
0x00, /* ........ */
/* --- new character '*' (42) */
0x00, /* ........ */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0xff, /* ######## */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x00, /* ........ */
0x00, /* ........ */
/* --- new character '+' (43) */
0x00, /* ........ */
0x18, /* ...##... */
0x18, /* ...##... */
0x7e, /* .######. */
0x18, /* ...##... */
0x18, /* ...##... */
0x00, /* ........ */
0x00, /* ........ */
/* --- new character ',' (44) */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x18, /* ...##... */
0x18, /* ...##... */
0x30, /* ..##.... */
/* --- new character '-' (45) */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x7e, /* .######. */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
/* --- new character '.' (46) */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
0x18, /* ...##... */
0x18, /* ...##... */
0x00, /* ........ */
/* --- new character '/' (47) */
0x00, /* ........ */
0x03, /* ......## */
0x06, /* .....##. */
0x0c, /* ....##.. */
0x18, /* ...##... */
0x30, /* ..##.... */
0x60, /* .##..... */
0x00, /* ........ */
/* --- new character '0' (48) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x6e, /* .##.###. */
0x76, /* .###.##. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character '1' (49) */
0x18, /* ...##... */
0x18, /* ...##... */
0x38, /* ..###... */
0x18, /* ...##... */
0x18, /* ...##... */
0x18, /* ...##... */
0x7e, /* .######. */
0x00, /* ........ */
/* --- new character '2' (50) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x06, /* .....##. */
0x0c, /* ....##.. */
0x30, /* ..##.... */
0x60, /* .##..... */
0x7e, /* .######. */
0x00, /* ........ */
/* --- new character '3' (51) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x06, /* .....##. */
0x1c, /* ...###.. */
0x06, /* .....##. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character '4' (52) */
0x06, /* .....##. */
0x0e, /* ....###. */
0x1e, /* ...####. */
0x66, /* .##..##. */
0x7f, /* .####### */
0x06, /* .....##. */
0x06, /* .....##. */
0x00, /* ........ */
/* --- new character '5' (53) */
0x7e, /* .######. */
0x60, /* .##..... */
0x7c, /* .#####.. */
0x06, /* .....##. */
0x06, /* .....##. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character '6' (54) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x60, /* .##..... */
0x7c, /* .#####.. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character '7' (55) */
0x7e, /* .######. */
0x66, /* .##..##. */
0x0c, /* ....##.. */
0x18, /* ...##... */
0x18, /* ...##... */
0x18, /* ...##... */
0x18, /* ...##... */
0x00, /* ........ */
/* --- new character '8' (56) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character '9' (57) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x3e, /* ..#####. */
0x06, /* .....##. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character ':' (58) */
0x00, /* ........ */
0x00, /* ........ */
0x18, /* ...##... */
0x00, /* ........ */
0x00, /* ........ */
0x18, /* ...##... */
0x00, /* ........ */
0x00, /* ........ */
/* --- new character ';' (59) */
0x00, /* ........ */
0x00, /* ........ */
0x18, /* ...##... */
0x00, /* ........ */
0x00, /* ........ */
0x18, /* ...##... */
0x18, /* ...##... */
0x30, /* ..##.... */
/* --- new character '<' (60) */
0x0e, /* ....###. */
0x18, /* ...##... */
0x30, /* ..##.... */
0x60, /* .##..... */
0x30, /* ..##.... */
0x18, /* ...##... */
0x0e, /* ....###. */
0x00, /* ........ */
/* --- new character '=' (61) */
0x00, /* ........ */
0x00, /* ........ */
0x7e, /* .######. */
0x00, /* ........ */
0x7e, /* .######. */
0x00, /* ........ */
0x00, /* ........ */
0x00, /* ........ */
/* --- new character '>' (62) */
0x70, /* .###.... */
0x18, /* ...##... */
0x0c, /* ....##.. */
0x06, /* .....##. */
0x0c, /* ....##.. */
0x18, /* ...##... */
0x70, /* .###.... */
0x00, /* ........ */
/* --- new character '?' (63) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x06, /* .....##. */
0x0c, /* ....##.. */
0x18, /* ...##... */
0x00, /* ........ */
0x18, /* ...##... */
0x00, /* ........ */
/* --- new character '@' (64) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x6e, /* .##.###. */
0x6e, /* .##.###. */
0x60, /* .##..... */
0x62, /* .##...#. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character 'A' (65) */
0x18, /* ...##... */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x7e, /* .######. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x00, /* ........ */
/* --- new character 'B' (66) */
0x7c, /* .#####.. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x7c, /* .#####.. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x7c, /* .#####.. */
0x00, /* ........ */
/* --- new character 'C' (67) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x60, /* .##..... */
0x60, /* .##..... */
0x60, /* .##..... */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character 'D' (68) */
0x78, /* .####... */
0x6c, /* .##.##.. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x6c, /* .##.##.. */
0x78, /* .####... */
0x00, /* ........ */
/* --- new character 'E' (69) */
0x7e, /* .######. */
0x60, /* .##..... */
0x60, /* .##..... */
0x78, /* .####... */
0x60, /* .##..... */
0x60, /* .##..... */
0x7e, /* .######. */
0x00, /* ........ */
/* --- new character 'F' (70) */
0x7e, /* .######. */
0x60, /* .##..... */
0x60, /* .##..... */
0x78, /* .####... */
0x60, /* .##..... */
0x60, /* .##..... */
0x60, /* .##..... */
0x00, /* ........ */
/* --- new character 'G' (71) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x60, /* .##..... */
0x6e, /* .##.###. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character 'H' (72) */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x7e, /* .######. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x00, /* ........ */
/* --- new character 'I' (73) */
0x3c, /* ..####.. */
0x18, /* ...##... */
0x18, /* ...##... */
0x18, /* ...##... */
0x18, /* ...##... */
0x18, /* ...##... */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character 'J' (74) */
0x1e, /* ...####. */
0x0c, /* ....##.. */
0x0c, /* ....##.. */
0x0c, /* ....##.. */
0x0c, /* ....##.. */
0x6c, /* .##.##.. */
0x38, /* ..###... */
0x00, /* ........ */
/* --- new character 'K' (75) */
0x66, /* .##..##. */
0x6c, /* .##.##.. */
0x78, /* .####... */
0x70, /* .###.... */
0x78, /* .####... */
0x6c, /* .##.##.. */
0x66, /* .##..##. */
0x00, /* ........ */
/* --- new character 'L' (76) */
0x60, /* .##..... */
0x60, /* .##..... */
0x60, /* .##..... */
0x60, /* .##..... */
0x60, /* .##..... */
0x60, /* .##..... */
0x7e, /* .######. */
0x00, /* ........ */
/* --- new character 'M' (77) */
0x63, /* .##...## */
0x77, /* .###.### */
0x7f, /* .####### */
0x6b, /* .##.#.## */
0x63, /* .##...## */
0x63, /* .##...## */
0x63, /* .##...## */
0x00, /* ........ */
/* --- new character 'N' (78) */
0x66, /* .##..##. */
0x76, /* .###.##. */
0x7e, /* .######. */
0x7e, /* .######. */
0x6e, /* .##.###. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x00, /* ........ */
/* --- new character 'O' (79) */
0x3c, /* ..####.. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x66, /* .##..##. */
0x3c, /* ..####.. */
0x00, /* ........ */
/* --- new character 'P' (80) */