forked from cellular-infrastructure/osmocom-analog
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"
This commit is contained in:
parent
a52c89800c
commit
496aff5a79
|
@ -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
|
||||
|
|
39
configure.ac
39
configure.ac
|
@ -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) */
|
||||
0x7c, /* .#####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x7c, /* .#####.. */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'Q' (81) */
|
||||
0x3c, /* ..####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3c, /* ..####.. */
|
||||
0x0e, /* ....###. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'R' (82) */
|
||||
0x7c, /* .#####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x7c, /* .#####.. */
|
||||
0x78, /* .####... */
|
||||
0x6c, /* .##.##.. */
|
||||
0x66, /* .##..##. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'S' (83) */
|
||||
0x3c, /* ..####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x60, /* .##..... */
|
||||
0x3c, /* ..####.. */
|
||||
0x06, /* .....##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3c, /* ..####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'T' (84) */
|
||||
0x7e, /* .######. */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'U' (85) */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3c, /* ..####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'V' (86) */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3c, /* ..####.. */
|
||||
0x18, /* ...##... */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'W' (87) */
|
||||
0x63, /* .##...## */
|
||||
0x63, /* .##...## */
|
||||
0x63, /* .##...## */
|
||||
0x6b, /* .##.#.## */
|
||||
0x7f, /* .####### */
|
||||
0x77, /* .###.### */
|
||||
0x63, /* .##...## */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'X' (88) */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3c, /* ..####.. */
|
||||
0x18, /* ...##... */
|
||||
0x3c, /* ..####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'Y' (89) */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3c, /* ..####.. */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'Z' (90) */
|
||||
0x7e, /* .######. */
|
||||
0x06, /* .....##. */
|
||||
0x0c, /* ....##.. */
|
||||
0x18, /* ...##... */
|
||||
0x30, /* ..##.... */
|
||||
0x60, /* .##..... */
|
||||
0x7e, /* .######. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character '[' (91) */
|
||||
0x3c, /* ..####.. */
|
||||
0x30, /* ..##.... */
|
||||
0x30, /* ..##.... */
|
||||
0x30, /* ..##.... */
|
||||
0x30, /* ..##.... */
|
||||
0x30, /* ..##.... */
|
||||
0x3c, /* ..####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character '\' (92) */
|
||||
0x00, /* ........ */
|
||||
0xc0, /* ##...... */
|
||||
0x60, /* .##..... */
|
||||
0x30, /* ..##.... */
|
||||
0x18, /* ...##... */
|
||||
0x0c, /* ....##.. */
|
||||
0x06, /* .....##. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character ']' (93) */
|
||||
0x3c, /* ..####.. */
|
||||
0x0c, /* ....##.. */
|
||||
0x0c, /* ....##.. */
|
||||
0x0c, /* ....##.. */
|
||||
0x0c, /* ....##.. */
|
||||
0x0c, /* ....##.. */
|
||||
0x3c, /* ..####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character '^' (94) */
|
||||
0x18, /* ...##... */
|
||||
0x3c, /* ..####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
/* --- new character '_' (95) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0xff, /* ######## */
|
||||
/* --- new character '`' (96) */
|
||||
0x60, /* .##..... */
|
||||
0x30, /* ..##.... */
|
||||
0x18, /* ...##... */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'a' (97) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x3c, /* ..####.. */
|
||||
0x06, /* .....##. */
|
||||
0x3e, /* ..#####. */
|
||||
0x66, /* .##..##. */
|
||||
0x3e, /* ..#####. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'b' (98) */
|
||||
0x00, /* ........ */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
0x7c, /* .#####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x7c, /* .#####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'c' (99) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x3c, /* ..####.. */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
0x3c, /* ..####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'd' (100) */
|
||||
0x00, /* ........ */
|
||||
0x06, /* .....##. */
|
||||
0x06, /* .....##. */
|
||||
0x3e, /* ..#####. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3e, /* ..#####. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'e' (101) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x3c, /* ..####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x7e, /* .######. */
|
||||
0x60, /* .##..... */
|
||||
0x3c, /* ..####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'f' (102) */
|
||||
0x00, /* ........ */
|
||||
0x0e, /* ....###. */
|
||||
0x18, /* ...##... */
|
||||
0x3e, /* ..#####. */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'g' (103) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x3e, /* ..#####. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3e, /* ..#####. */
|
||||
0x06, /* .....##. */
|
||||
0x7c, /* .#####.. */
|
||||
/* --- new character 'h' (104) */
|
||||
0x00, /* ........ */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
0x7c, /* .#####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'i' (105) */
|
||||
0x00, /* ........ */
|
||||
0x18, /* ...##... */
|
||||
0x00, /* ........ */
|
||||
0x38, /* ..###... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x3c, /* ..####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'j' (106) */
|
||||
0x00, /* ........ */
|
||||
0x06, /* .....##. */
|
||||
0x00, /* ........ */
|
||||
0x06, /* .....##. */
|
||||
0x06, /* .....##. */
|
||||
0x06, /* .....##. */
|
||||
0x06, /* .....##. */
|
||||
0x3c, /* ..####.. */
|
||||
/* --- new character 'k' (107) */
|
||||
0x00, /* ........ */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
0x6c, /* .##.##.. */
|
||||
0x78, /* .####... */
|
||||
0x6c, /* .##.##.. */
|
||||
0x66, /* .##..##. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'l' (108) */
|
||||
0x00, /* ........ */
|
||||
0x38, /* ..###... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x3c, /* ..####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'm' (109) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x66, /* .##..##. */
|
||||
0x7f, /* .####### */
|
||||
0x7f, /* .####### */
|
||||
0x6b, /* .##.#.## */
|
||||
0x63, /* .##...## */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'n' (110) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x7c, /* .#####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'o' (111) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x3c, /* ..####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3c, /* ..####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'p' (112) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x7c, /* .#####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x7c, /* .#####.. */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
/* --- new character 'q' (113) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x3e, /* ..#####. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3e, /* ..#####. */
|
||||
0x06, /* .....##. */
|
||||
0x06, /* .....##. */
|
||||
/* --- new character 'r' (114) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x7c, /* .#####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
0x60, /* .##..... */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 's' (115) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x3e, /* ..#####. */
|
||||
0x60, /* .##..... */
|
||||
0x3c, /* ..####.. */
|
||||
0x06, /* .....##. */
|
||||
0x7c, /* .#####.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 't' (116) */
|
||||
0x00, /* ........ */
|
||||
0x18, /* ...##... */
|
||||
0x7e, /* .######. */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x0e, /* ....###. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'u' (117) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3e, /* ..#####. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'v' (118) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3c, /* ..####.. */
|
||||
0x18, /* ...##... */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'w' (119) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x63, /* .##...## */
|
||||
0x6b, /* .##.#.## */
|
||||
0x7f, /* .####### */
|
||||
0x3e, /* ..#####. */
|
||||
0x36, /* ..##.##. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'x' (120) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x66, /* .##..##. */
|
||||
0x3c, /* ..####.. */
|
||||
0x18, /* ...##... */
|
||||
0x3c, /* ..####.. */
|
||||
0x66, /* .##..##. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character 'y' (121) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x66, /* .##..##. */
|
||||
0x3e, /* ..#####. */
|
||||
0x0c, /* ....##.. */
|
||||
0x78, /* .####... */
|
||||
/* --- new character 'z' (122) */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x7e, /* .######. */
|
||||
0x0c, /* ....##.. */
|
||||
0x18, /* ...##... */
|
||||
0x30, /* ..##.... */
|
||||
0x7e, /* .######. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character '{' (123) */
|
||||
0x1c, /* ...###.. */
|
||||
0x30, /* ..##.... */
|
||||
0x30, /* ..##.... */
|
||||
0x60, /* .##..... */
|
||||
0x30, /* ..##.... */
|
||||
0x30, /* ..##.... */
|
||||
0x1c, /* ...###.. */
|
||||
0x00, /* ........ */
|
||||
/* --- new character '|' (124) */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x18, /* ...##... */
|
||||
0x00, /* ........ */
|
||||
/* --- new character '}' (125) */
|
||||
0x38, /* ..###... */
|
||||
0x0c, /* ....##.. */
|
||||
0x0c, /* ....##.. */
|
||||
0x06, /* .....##. */
|
||||
0x0c, /* ....##.. */
|
||||
0x0c, /* ....##.. */
|
||||
0x38, /* ..###... */
|
||||
0x00, /* ........ */
|
||||
/* --- new character '~' (126) */
|
||||
0x33, /* ..##..## */
|
||||
0x7e, /* .######. */
|
||||
0xcc, /* ##..##.. */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
0x00, /* ........ */
|
||||
/* --- new character '' (127) */
|
||||
0x00, /* ........ */
|
||||
0x08, /* ....#... */
|
||||
0x0c, /* ....##.. */
|
||||
0xfe, /* #######. */
|
||||
0xfe, /* #######. */
|
||||
0x0c, /* ....##.. */
|
||||
0x08, /* ....#... */
|
||||
0x00, /* ........ */
|
||||
};
|
||||
|
||||
const uint8_t *get_font(char c)
|
||||
{
|
||||
if (c < 32) /* implies c > 127 */
|
||||
c = 32;
|
||||
return font_c64_data + 8 * (int)(c - 32);
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
const uint8_t *get_font(char c);
|
||||
|
|
@ -0,0 +1,511 @@
|
|||
/* FUBK test image generator
|
||||
*
|
||||
* (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 <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "../common/sample.h"
|
||||
#include "fubk.h"
|
||||
#include "font.h"
|
||||
|
||||
#define GRID_LINES 4
|
||||
#define GRID_HEIGHT 40
|
||||
#define CENTER_LINE (287 - 2)
|
||||
|
||||
#define GRID_WIDTH 0.0000027
|
||||
#define RAMP_WIDTH 0.0000002
|
||||
|
||||
#define GRID_LEVEL 1.0
|
||||
#define FIELD_LEVEL 0.25
|
||||
|
||||
#define CIRCLE_LEVEL 1.0
|
||||
#define CIRCLE_CENTER (287 - 1)
|
||||
#define CIRCLE_HEIGTH 3
|
||||
|
||||
struct color_bar {
|
||||
double amplitude, phase;
|
||||
} color_bar[8] = {
|
||||
{0.0, 0.0},
|
||||
{0.336, 167.1},
|
||||
{0.474, 283.5},
|
||||
{0.443, 240.7},
|
||||
{0.443, 60.7},
|
||||
{0.474, 103.5},
|
||||
{0.336, 347.1},
|
||||
{0.0, 0.0},
|
||||
};
|
||||
|
||||
struct multi_burst {
|
||||
double width; /* how whide is this portion */
|
||||
double level; /* level of this portion */
|
||||
double frequency; /* frequency of burst or zero */
|
||||
double V; /* amplitude of V */
|
||||
} multi_burst[10] = {
|
||||
{ 3.9, 1.0, 0.0, 0.0 }, /* white */
|
||||
{ 0.7, 0.5, 0.0, 0.0 },
|
||||
{ 5.0, 0.5, 1000000.0, 0.0 }, /* 1 MHz */
|
||||
{ 0.7, 0.5, 0.0, 0.0 },
|
||||
{ 5.0, 0.5, 2000000.0, 0.0 }, /* 2 MHz */
|
||||
{ 2.0, 0.5, 0.0, 0.0 },
|
||||
{ 5.0, 0.5, 3000000.0, 0.0 }, /* 3 MHz */
|
||||
{ 0.7, 0.5, 0.0, 0.0 },
|
||||
{ 7.9, 0.5, 0.0, 0.5 }, /* Color burst */
|
||||
{ 1.5, 0.5, 0.0, 0.0 },
|
||||
/* note that the color burst ist shifted left by 0.4uS when delaying Y.
|
||||
* this makes color burst appear left of the center.
|
||||
* we don't compensate this here, since i have seen this shift in real generators. */
|
||||
};
|
||||
|
||||
/* create cosine ramp, so that the bandwidth is not higher than 2 * width of the ramp(0..1) */
|
||||
static inline double ramp(double x)
|
||||
{
|
||||
return 0.5 - 0.5 * cos(x * M_PI);
|
||||
}
|
||||
|
||||
static double mittelfeld(sample_t *sample, double samplerate, int *_i, double *_x, double render_start, double step, sample_t *color_u, sample_t *color_v, int v_polarity, int line, const char *station_id)
|
||||
{
|
||||
double render_end, x = *_x, vline_x = *_x, vline_start = render_start;
|
||||
int i = *_i, vline_i = *_i, b;
|
||||
double amplitude, phase, phase_step, Y, U, V, colorphase;
|
||||
int position = line / GRID_HEIGHT;
|
||||
int char_line;
|
||||
uint8_t bits;
|
||||
int bit, last_bit, c;
|
||||
|
||||
switch (position) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
/* color bars */
|
||||
for (b = 0; b < 8; b++) {
|
||||
Y = (1.0 - (double)b / 7.0) * 0.75;
|
||||
amplitude = color_bar[b].amplitude;
|
||||
if (v_polarity < 0)
|
||||
colorphase = (360.0 - color_bar[b].phase) / 180.0 * M_PI;
|
||||
else
|
||||
colorphase = color_bar[b].phase / 180.0 * M_PI;
|
||||
U = cos(colorphase) * amplitude / 2.0;
|
||||
V = sin(colorphase) * amplitude / 2.0;
|
||||
render_end = render_start + GRID_WIDTH * 1.5;
|
||||
while (x < render_end) {
|
||||
color_u[i] = U;
|
||||
color_v[i] = V;
|
||||
sample[i++] = Y;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
/* gray steps */
|
||||
for (b = 0; b < 5; b++) {
|
||||
Y = (double)b / 4.0;
|
||||
render_end = render_start + GRID_WIDTH * 2.4;
|
||||
while (x < render_end) {
|
||||
sample[i++] = Y;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
/* station ID */
|
||||
/* white bar before text */
|
||||
render_end = render_start + GRID_WIDTH * 2.4;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 1.0;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
/* black bar before text (exclude one ramp width) */
|
||||
render_end = render_start + GRID_WIDTH * 2.4 / 54.0 - RAMP_WIDTH / 2.0;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 0.0;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
last_bit = 0;
|
||||
/* text */
|
||||
/* 12 chars, plus one extra bit */
|
||||
for (c = 0; c < 13; c++) {
|
||||
char_line = ((line % GRID_HEIGHT) + 1) / 4 - 2;
|
||||
if (char_line < 0 || char_line > 7 || c == 12)
|
||||
bits = 0x00;
|
||||
else
|
||||
bits = get_font(station_id[c])[char_line];
|
||||
for (b = 0; b < 8; b++) {
|
||||
bit = ((bits << b) & 128);
|
||||
if (!last_bit && !bit) {
|
||||
/* keep black */
|
||||
render_end = render_start + RAMP_WIDTH;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 0.0;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
}
|
||||
if (last_bit && bit) {
|
||||
/* keep white */
|
||||
render_end = render_start + RAMP_WIDTH;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 1.0;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
}
|
||||
if (!last_bit && bit) {
|
||||
/* ramp to white */
|
||||
render_end = render_start + RAMP_WIDTH;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 1.0 - ramp((render_end - x) / RAMP_WIDTH);
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
}
|
||||
if (last_bit && !bit) {
|
||||
/* ramp to black */
|
||||
render_end = render_start + RAMP_WIDTH;
|
||||
while (x < render_end) {
|
||||
sample[i++] = ramp((render_end - x) / RAMP_WIDTH);
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
}
|
||||
last_bit = bit;
|
||||
/* after extra bit after 12 chars, stop rendering */
|
||||
if (c == 12)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* black bar after text */
|
||||
render_end = render_start + GRID_WIDTH * 2.4 / 54.0 - RAMP_WIDTH / 2.0;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 0.0;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
/* white bar after text */
|
||||
render_end = render_start + GRID_WIDTH * 2.4;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 1.0;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
break;
|
||||
case 6:
|
||||
/* multi-burst */
|
||||
for (b = 0; b < 10; b++) {
|
||||
Y = multi_burst[b].level;
|
||||
V = multi_burst[b].V;
|
||||
phase_step = multi_burst[b].frequency * 2.0 * M_PI / samplerate;
|
||||
phase = 0.0;
|
||||
render_end = render_start + multi_burst[b].width / 1e6;
|
||||
while (x < render_end) {
|
||||
if (v_polarity < 0)
|
||||
colorphase = (360.0 - 145.9) / 180.0 * M_PI;
|
||||
else
|
||||
colorphase = 145.9 / 180.0 * M_PI;
|
||||
color_u[i] = cos(colorphase) * V / 2.0;
|
||||
color_v[i] = sin(colorphase) * V / 2.0;
|
||||
sample[i++] = Y + sin(phase) / 2.0;
|
||||
phase += phase_step;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
/* white bar with black pulse triangle */
|
||||
/* white bar */
|
||||
render_end = render_start + GRID_WIDTH * 6 - 0.0000005 - RAMP_WIDTH;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 1.0;
|
||||
x += step;
|
||||
}
|
||||
/* ramp to triangle */
|
||||
render_end += RAMP_WIDTH;
|
||||
while (x < render_end) {
|
||||
sample[i++] = ramp((render_end - x) / RAMP_WIDTH);
|
||||
x += step;
|
||||
}
|
||||
/* triangle */
|
||||
render_end += 0.000001 * (1.0 - ((double)(line % GRID_HEIGHT) / (double)GRID_HEIGHT));
|
||||
while (x < render_end) {
|
||||
sample[i++] = 0.0;
|
||||
x += step;
|
||||
}
|
||||
/* ramp from triangle */
|
||||
render_end += RAMP_WIDTH;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 1.0 - ramp((render_end - x) / RAMP_WIDTH);
|
||||
x += step;
|
||||
}
|
||||
/* white bar */
|
||||
render_end = render_start + GRID_WIDTH * 12;
|
||||
while (x < render_end) {
|
||||
sample[i++] = 1.0;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
break;
|
||||
case 8:
|
||||
/* sawtooth +-V and uncolored field */
|
||||
render_end = render_start + GRID_WIDTH * 8;
|
||||
while (x < render_end) {
|
||||
Y = (render_end - x) / (render_end - render_start) / 2;
|
||||
if (Y > 0.375) Y = 0.375;
|
||||
color_v[i] = (double)v_polarity * Y;
|
||||
sample[i++] = Y;
|
||||
x += step;
|
||||
}
|
||||
uncolored:
|
||||
/* uncolored +V field */
|
||||
render_end = render_start + GRID_WIDTH * 10;
|
||||
while (x < render_end) {
|
||||
color_v[i] = 0.375;
|
||||
sample[i++] = 0.375;
|
||||
x += step;
|
||||
}
|
||||
/* uncolored +-U field */
|
||||
render_end = render_start + GRID_WIDTH * 12;
|
||||
while (x < render_end) {
|
||||
color_u[i] = (double)v_polarity * 0.375;
|
||||
sample[i++] = 0.375;
|
||||
x += step;
|
||||
}
|
||||
render_start = render_end;
|
||||
break;
|
||||
case 9:
|
||||
/* sawtooth +U and uncolored field */
|
||||
render_end = render_start + GRID_WIDTH * 8;
|
||||
while (x < render_end) {
|
||||
Y = (render_end - x) / (render_end - render_start) / 2;
|
||||
if (Y > 0.375) Y = 0.375;
|
||||
color_u[i] = Y;
|
||||
sample[i++] = Y;
|
||||
x += step;
|
||||
}
|
||||
goto uncolored;
|
||||
}
|
||||
|
||||
/* draw vertical line */
|
||||
if (position >= 3 && position <= 6) {
|
||||
render_end = vline_start + GRID_WIDTH * 6 - RAMP_WIDTH;
|
||||
while (vline_x < render_end) {
|
||||
vline_i++;
|
||||
vline_x += step;
|
||||
}
|
||||
render_end += RAMP_WIDTH;
|
||||
while (vline_x < render_end) {
|
||||
sample[vline_i] = ramp((render_end - vline_x) / RAMP_WIDTH) * (sample[vline_i] - GRID_LEVEL) + GRID_LEVEL;
|
||||
vline_i++;
|
||||
vline_x += step;
|
||||
}
|
||||
render_end += RAMP_WIDTH;
|
||||
while (vline_x < render_end) {
|
||||
sample[vline_i] = ramp((render_end - vline_x) / RAMP_WIDTH) * (GRID_LEVEL - sample[vline_i]) + sample[vline_i];
|
||||
vline_i++;
|
||||
vline_x += step;
|
||||
}
|
||||
}
|
||||
|
||||
*_x = x;
|
||||
*_i = i;
|
||||
return render_start;
|
||||
}
|
||||
|
||||
static void draw_line(sample_t *sample, sample_t *color_u, sample_t *color_v, int i, double step, double x, double line_end, double x_start, double x_stop)
|
||||
{
|
||||
double render_start, render_end;
|
||||
|
||||
/* go to ramp start */
|
||||
render_start = x_start - RAMP_WIDTH;
|
||||
while (x < render_start && x < line_end) {
|
||||
i++;
|
||||
x += step;
|
||||
}
|
||||
/* ramp up to line */
|
||||
render_end = render_start + RAMP_WIDTH;
|
||||
while (x < render_end && x < line_end) {
|
||||
color_u[i] = color_v[i] = 0;
|
||||
sample[i] = ramp((x - render_start) / RAMP_WIDTH) * (CIRCLE_LEVEL - sample[i]) + sample[i];
|
||||
i++;
|
||||
x += step;
|
||||
}
|
||||
/* draw line */
|
||||
render_start = x_stop;
|
||||
while (x < render_start && x < line_end) {
|
||||
color_u[i] = color_v[i] = 0;
|
||||
sample[i++] = CIRCLE_LEVEL;
|
||||
x += step;
|
||||
}
|
||||
/* ramp down from line */
|
||||
render_end = render_start + RAMP_WIDTH;
|
||||
while (x < render_end && x < line_end) {
|
||||
color_u[i] = color_v[i] = 0;
|
||||
sample[i] = ramp((x - render_start) / RAMP_WIDTH) * (sample[i] - CIRCLE_LEVEL) + CIRCLE_LEVEL;
|
||||
i++;
|
||||
x += step;
|
||||
}
|
||||
}
|
||||
|
||||
static void kreislinie(sample_t *sample, sample_t *color_u, sample_t *color_v, int i, double step, double x, double line_end, int line, double circle_radius)
|
||||
{
|
||||
double dist_center = abs(CIRCLE_CENTER - line), y, oc, ic;
|
||||
double center_x = (line_end - x) / 2.0 + x;
|
||||
|
||||
/* no radius, no circle */
|
||||
if (circle_radius < 0.1)
|
||||
return;
|
||||
|
||||
/* check if we are above or below outer circle */
|
||||
y = dist_center / ((double)GRID_HEIGHT * circle_radius);
|
||||
if (y > 1.0)
|
||||
return;
|
||||
|
||||
/* calc outer circle */
|
||||
oc = sqrt(2*(y+1) - (y+1)*(y+1)) * GRID_WIDTH * circle_radius + (double)(line & 1)/40000000.0;
|
||||
|
||||
/* check if we are above or below inner circle */
|
||||
y = dist_center / ((double)GRID_HEIGHT * circle_radius - CIRCLE_HEIGTH);
|
||||
if (y > 1.0) {
|
||||
/* draw outer circle only */
|
||||
draw_line(sample, color_u, color_v, i, step, x, line_end, center_x - oc, center_x + oc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* calc inner circle */
|
||||
ic = sqrt(2*(y+1) - (y+1)*(y+1)) * GRID_WIDTH * circle_radius;
|
||||
|
||||
/* draw both circles */
|
||||
draw_line(sample, color_u, color_v, i, step, x, line_end, center_x - oc, center_x - ic);
|
||||
draw_line(sample, color_u, color_v, i, step, x, line_end, center_x + ic, center_x + oc);
|
||||
}
|
||||
|
||||
/* render test image line starting with x and end with LINE_LENGTH
|
||||
*
|
||||
* sample: pointer to samples, starting at x = 0
|
||||
* samplerate: is the sample rate in hertz
|
||||
* x: at what x to start rendering
|
||||
* line: line no. to render
|
||||
*/
|
||||
int fubk_gen_line(sample_t *sample, double x, double samplerate, sample_t *color_u, sample_t *color_v, int v_polarity, double line_start, double line_end, int line, double radius, int color_bar, int grid_only, const char *station_id)
|
||||
{
|
||||
double initial_x;
|
||||
double step = 1.0 / samplerate;
|
||||
int i = 0, initial_i;
|
||||
double render_start, render_end, center_x;
|
||||
int grid_count = 0;
|
||||
int in_mittelfeld;
|
||||
|
||||
/* skip x to line_start */
|
||||
while (x < line_start && x < line_end) {
|
||||
i++;
|
||||
x += step;
|
||||
}
|
||||
if (x >= line_end)
|
||||
return i;
|
||||
initial_i = i;
|
||||
initial_x = x;
|
||||
|
||||
/* calculate phase for ramp start of center line */
|
||||
center_x = (line_end - line_start) / 2.0 + line_start;
|
||||
|
||||
/* check if we are are rendering middle field and set line number */
|
||||
if (grid_only)
|
||||
in_mittelfeld = -1;
|
||||
else if (color_bar)
|
||||
in_mittelfeld = 0;
|
||||
else {
|
||||
in_mittelfeld = line - (CENTER_LINE - GRID_HEIGHT*5) -GRID_LINES/2;
|
||||
if (in_mittelfeld >= GRID_HEIGHT*10)
|
||||
in_mittelfeld = -1;
|
||||
}
|
||||
|
||||
/* calculate position in grid:
|
||||
* get the distance below the center line (line - CENTER_LINE)
|
||||
* then be sure not to get negative value: add a multiple of the grid period
|
||||
* then use modulo to get the distance below the grid line */
|
||||
if (((line - CENTER_LINE + GRID_HEIGHT*20) % GRID_HEIGHT) < GRID_LINES) {
|
||||
if (in_mittelfeld < 0)
|
||||
goto no_mittelfeld;
|
||||
/* special case where the center line is always drawn */
|
||||
if (in_mittelfeld >= 198 && in_mittelfeld <= 201)
|
||||
goto no_mittelfeld;
|
||||
/* grid line before middle field */
|
||||
render_end = center_x - GRID_WIDTH*6.0;
|
||||
while (x < render_end && x < line_end) {
|
||||
sample[i++] = GRID_LEVEL;
|
||||
x += step;
|
||||
}
|
||||
if (x >= line_end)
|
||||
return i;
|
||||
render_start = render_end;
|
||||
/* middle field */
|
||||
render_start = mittelfeld(sample, samplerate, &i, &x, render_start, step, color_u, color_v, v_polarity, in_mittelfeld, station_id);
|
||||
no_mittelfeld:
|
||||
/* grid line after middle field */
|
||||
while (x < line_end) {
|
||||
sample[i++] = GRID_LEVEL;
|
||||
x += step;
|
||||
}
|
||||
} else {
|
||||
while (1) {
|
||||
/* calculate position for next ramp:
|
||||
* get the distance to center (center_x - x - RAMP_WIDTH)
|
||||
* then be sure not to get negative value: add a multiple of the grid period
|
||||
* then use fmod to get the next ramp start */
|
||||
render_start = fmod(center_x - x - RAMP_WIDTH + GRID_WIDTH*20.0, GRID_WIDTH) + x;
|
||||
/* draw background field up to grid start */
|
||||
while (x < render_start && x < line_end) {
|
||||
sample[i++] = FIELD_LEVEL;
|
||||
x += step;
|
||||
}
|
||||
if (x >= line_end)
|
||||
break;
|
||||
/* ramp up to grid level */
|
||||
render_end = render_start + RAMP_WIDTH;
|
||||
while (x < render_end && x < line_end) {
|
||||
sample[i++] = ramp((x - render_start) / RAMP_WIDTH) * (GRID_LEVEL - FIELD_LEVEL) + FIELD_LEVEL;
|
||||
x += step;
|
||||
}
|
||||
if (x >= line_end)
|
||||
break;
|
||||
render_start = render_end;
|
||||
/* middle field */
|
||||
if (in_mittelfeld >= 0) {
|
||||
if (++grid_count == 4)
|
||||
render_start = mittelfeld(sample, samplerate, &i, &x, render_start, step, color_u, color_v, v_polarity, in_mittelfeld, station_id);
|
||||
}
|
||||
/* ramp down to field level */
|
||||
render_end = render_start + RAMP_WIDTH;
|
||||
while (x < render_end && x < line_end) {
|
||||
sample[i++] = ramp((x - render_start) / RAMP_WIDTH) * (FIELD_LEVEL - GRID_LEVEL) + GRID_LEVEL;
|
||||
x += step;
|
||||
}
|
||||
if (x >= line_end)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kreislinie(sample, color_u, color_v, initial_i, step, initial_x, line_end, line, radius);
|
||||
|
||||
return i;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
int fubk_gen_line(sample_t *sample, double x, double samplerate, sample_t *color_u, sample_t *color_v, int v_polarity, double frame_start, double line_end, int line, double circle_radius, int color_bar, int grid_only, const char *station_id);
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/* Pixle image / PAL conversion
|
||||
*
|
||||
* (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 <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "../common/sample.h"
|
||||
#include "image.h"
|
||||
|
||||
/* render image line starting with x and end with LINE_LENGTH
|
||||
*
|
||||
* sample: pointer to samples, starting at x = 0
|
||||
* samplerate: is the sample rate in hertz
|
||||
* x: at what x to start rendering
|
||||
* line: line no. to render
|
||||
*/
|
||||
int image_gen_line(sample_t *sample, double x, double samplerate, sample_t *color_u, sample_t *color_v, int v_polarity, double line_start, double line_end, unsigned short *img, int width)
|
||||
{
|
||||
double img_x = 0;
|
||||
double step = 1.0 / samplerate;
|
||||
double img_step = (double)width / (samplerate * (line_end - line_start));
|
||||
int i = 0;
|
||||
double R, G, B, Y, U, V;
|
||||
|
||||
/* skip x to line_start */
|
||||
while (x < line_start && x < line_end) {
|
||||
i++;
|
||||
x += step;
|
||||
}
|
||||
if (x >= line_end)
|
||||
return i;
|
||||
|
||||
/* draw pixle into image */
|
||||
while (x < line_end) {
|
||||
R = (double)(img[(int)img_x*3+0]) / 65535.0;
|
||||
G = (double)(img[(int)img_x*3+1]) / 65535.0;
|
||||
B = (double)(img[(int)img_x*3+2]) / 65535.0;
|
||||
Y = 0.299 * R + 0.587 * G + 0.114 * B;
|
||||
U = 0.492 * (B - Y);
|
||||
V = 0.877 * (R - Y);
|
||||
sample[i] = Y;
|
||||
color_u[i] = U;
|
||||
color_v[i] = V * (double)v_polarity;
|
||||
i++;
|
||||
x += step;
|
||||
img_x += img_step;
|
||||
if ((int)img_x == width)
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
int image_gen_line(sample_t *sample, double x, double samplerate, sample_t *color_u, sample_t *color_v, int v_polarity, double line_start, double line_end, unsigned short *img, int width);
|
||||
|
|
@ -0,0 +1,492 @@
|
|||
/* main function
|
||||
*
|
||||
* (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/>.
|
||||
*/
|
||||
|
||||
enum paging_signal;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <math.h>
|
||||
#include "../common/sample.h"
|
||||
#include "../common/iir_filter.h"
|
||||
#include "../common/fm_modulation.h"
|
||||
#include "../common/wave.h"
|
||||
#include "../common/img.h"
|
||||
#include "../common/debug.h"
|
||||
#ifdef HAVE_SDR
|
||||
#include "../common/sdr_config.h"
|
||||
#include "../common/sdr.h"
|
||||
#endif
|
||||
#include "bas.h"
|
||||
#include "tv_modulate.h"
|
||||
#include "channels.h"
|
||||
|
||||
int use_sdr = 0;
|
||||
int num_kanal = 1; /* only one channel used for debugging */
|
||||
|
||||
void clear_console_text() {}
|
||||
void print_console_text() {}
|
||||
void display_status_limit_scroll() {}
|
||||
|
||||
static double __attribute__((__unused__)) modulation = 0.7; /* level of modulation for I/Q amplitudes */
|
||||
static double frequency = 0.0;
|
||||
static int fbas = 1;
|
||||
static int tone = 1;
|
||||
static double circle_radius = 6.7;
|
||||
static int color_bar = 0;
|
||||
static int grid_only = 0;
|
||||
static const char *station_id = "Jolly Roger";
|
||||
static int __attribute__((__unused__)) latency = 30;
|
||||
static double samplerate = 10e6;
|
||||
static const char *wave_file = NULL;
|
||||
|
||||
/* global variable to quit main loop */
|
||||
int quit = 0;
|
||||
|
||||
void sighandler(int sigset)
|
||||
{
|
||||
if (sigset == SIGHUP)
|
||||
return;
|
||||
if (sigset == SIGPIPE)
|
||||
return;
|
||||
|
||||
// clear_console_text();
|
||||
printf("Signal received: %d\n", sigset);
|
||||
|
||||
quit = 1;
|
||||
}
|
||||
|
||||
void print_help(const char *arg0)
|
||||
|
||||
{
|
||||
printf("Usage: %s -f <frequency> | -c <channel> <command>\n", arg0);
|
||||
/* - - */
|
||||
printf("\ncommand:\n");
|
||||
printf(" tx-fubk Transmit FUBK test image (German PAL image)\n");
|
||||
printf(" tx-vcr Transmit VCR calibration pattern\n");
|
||||
printf(" tx-img <image> Transmit given image file\n");
|
||||
printf(" Use 4:3 image with 574 lines for best result.\n");
|
||||
printf("\ngeneral options:\n");
|
||||
printf(" -f --frequency <frequency>\n");
|
||||
printf(" Give frequency in Hertz.\n");
|
||||
printf(" -c --channel <channel>\n");
|
||||
printf(" Or give channel number.\n");
|
||||
printf(" Use 'list' to get a channel list.\n");
|
||||
printf(" -r --samplerate <frequency>\n");
|
||||
printf(" Give sample rate in Hertz.\n");
|
||||
printf(" -w --wave-file <filename>\n");
|
||||
printf(" Output to wave file instead of SDR\n");
|
||||
printf("\nsignal options:\n");
|
||||
printf(" -F --fbas 1 | 0\n");
|
||||
printf(" Turn color on or off. (default = %d)\n", fbas);
|
||||
printf(" -T --tone 1 | 0\n");
|
||||
printf(" Turn tone on or off. (default = %d)\n", tone);
|
||||
printf("\nFUBK options:\n");
|
||||
printf(" -R --circle-radius <radius> | 0\n");
|
||||
printf(" Use circle radius or 0 for off. (default = %.1f)\n", circle_radius);
|
||||
printf(" -C --color-bar 1 | 0\n");
|
||||
printf(" 1 == Color bar on, 0 = Show complete middle field. (default = %d)\n", color_bar);
|
||||
printf(" For clean scope signal, also turn off circle by setting radius to 0.\n");
|
||||
printf(" -G --grid-only 1 | 0\n");
|
||||
printf(" 1 == Show only the grid of the test image. (default = %d)\n", grid_only);
|
||||
printf(" -I --sation-id \"<text>\"\n");
|
||||
printf(" Give exactly 12 characters to display as Station ID.\n");
|
||||
printf(" (default = \"%s\")\n", station_id);
|
||||
#ifdef HAVE_SDR
|
||||
sdr_config_print_help();
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct option long_options_common[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"frequency", 1, 0, 'f'},
|
||||
{"channel", 1, 0, 'c'},
|
||||
{"samplerate", 1, 0, 'r'},
|
||||
{"wave-file", 1, 0, 'w'},
|
||||
{"fbas", 1, 0, 'F'},
|
||||
{"tone", 1, 0, 'T'},
|
||||
{"circle-radius", 1, 0, 'R'},
|
||||
{"color-bar", 1, 0, 'C'},
|
||||
{"grid-only", 1, 0, 'G'},
|
||||
{"station-id", 1, 0, 'I'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static const char *optstring_common = "hf:c:r:w:F:T:R:C:G:I:";
|
||||
|
||||
struct option *long_options;
|
||||
char *optstring;
|
||||
|
||||
static void check_duplicate_option(int num, struct option *option)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
if (long_options[i].val == option->val) {
|
||||
fprintf(stderr, "Duplicate option %d. Please fix!\n", option->val);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void set_options_common(void)
|
||||
{
|
||||
int i = 0, j;
|
||||
|
||||
long_options = calloc(sizeof(*long_options), 256);
|
||||
for (j = 0; long_options_common[i].name; i++, j++) {
|
||||
check_duplicate_option(i, &long_options_common[j]);
|
||||
memcpy(&long_options[i], &long_options_common[j], sizeof(*long_options));
|
||||
}
|
||||
#ifdef HAVE_SDR
|
||||
for (j = 0; sdr_config_long_options[j].name; i++, j++) {
|
||||
check_duplicate_option(i, &sdr_config_long_options[j]);
|
||||
memcpy(&long_options[i], &sdr_config_long_options[j], sizeof(*long_options));
|
||||
}
|
||||
#endif
|
||||
|
||||
optstring = calloc(256, 2);
|
||||
strcpy(optstring, optstring_common);
|
||||
#ifdef HAVE_SDR
|
||||
strcat(optstring, sdr_config_optstring);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int handle_options(int argc, char **argv)
|
||||
{
|
||||
int skip_args = 0;
|
||||
#ifdef HAVE_SDR
|
||||
int rc;
|
||||
#endif
|
||||
|
||||
set_options_common();
|
||||
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
|
||||
c = getopt_long(argc, argv, optstring, long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_help(argv[0]);
|
||||
exit(0);
|
||||
case 'f':
|
||||
frequency = atof(optarg);
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'c':
|
||||
if (!strcmp(optarg, "list")) {
|
||||
list_tv_channels();
|
||||
exit(0);
|
||||
}
|
||||
frequency = get_tv_video_frequency(atoi(optarg));
|
||||
if (frequency == 0.0) {
|
||||
fprintf(stderr, "Given channel number unknown, use \"-c list\" to get a list.\n");
|
||||
exit(0);
|
||||
}
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'r':
|
||||
samplerate = atof(optarg);
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'w':
|
||||
wave_file = strdup(optarg);
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'F':
|
||||
fbas = atoi(optarg);
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'T':
|
||||
tone = atoi(optarg);
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'R':
|
||||
circle_radius = atof(optarg);
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'C':
|
||||
color_bar = atoi(optarg);
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'G':
|
||||
grid_only = atoi(optarg);
|
||||
skip_args += 2;
|
||||
break;
|
||||
case 'I':
|
||||
station_id = strdup(optarg);
|
||||
if (strlen(station_id) != 12) {
|
||||
fprintf(stderr, "Given station ID must be exactly 12 charaters long. (Use spaces to fill it.)\n");
|
||||
exit(0);
|
||||
}
|
||||
skip_args += 2;
|
||||
break;
|
||||
default:
|
||||
#ifdef HAVE_SDR
|
||||
rc = sdr_config_opt_switch(c, &skip_args);
|
||||
if (rc < 0)
|
||||
exit(0);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return skip_args;
|
||||
}
|
||||
|
||||
static void tx_bas(sample_t *sample_bas, __attribute__((__unused__)) sample_t *sample_tone, uint8_t *power_tone, int samples)
|
||||
{
|
||||
/* catch signals */
|
||||
signal(SIGINT, sighandler);
|
||||
signal(SIGHUP, sighandler);
|
||||
signal(SIGTERM, sighandler);
|
||||
signal(SIGPIPE, sighandler);
|
||||
|
||||
if (wave_file) {
|
||||
wave_rec_t rec;
|
||||
int rc;
|
||||
sample_t *buffers[1];
|
||||
|
||||
rc = wave_create_record(&rec, wave_file, samplerate, 1, 1.0);
|
||||
if (rc < 0) {
|
||||
// FIXME cleanup
|
||||
exit(0);
|
||||
}
|
||||
|
||||
buffers[0] = sample_bas;
|
||||
wave_write(&rec, buffers, samples);
|
||||
|
||||
wave_destroy_record(&rec);
|
||||
} else {
|
||||
#ifdef HAVE_SDR
|
||||
float *buff = NULL;
|
||||
void *sdr = NULL;
|
||||
int latspl = samplerate * latency / 1000;
|
||||
float *sendbuff;
|
||||
|
||||
sendbuff = calloc(latspl * 2, sizeof(*sendbuff));
|
||||
if (!sendbuff) {
|
||||
fprintf(stderr, "No mem!\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* modulate */
|
||||
buff = calloc(samples + 10.0, sizeof(sample_t) * 2);
|
||||
if (!buff) {
|
||||
fprintf(stderr, "No mem!\n");
|
||||
goto error;
|
||||
}
|
||||
tv_modulate(buff, samples, sample_bas, modulation);
|
||||
|
||||
if (sample_tone) {
|
||||
/* bandwidth is 2*(deviation + 2*f(sig)) = 2 * (50 + 2*15) = 160khz */
|
||||
fm_mod_t mod;
|
||||
fm_mod_init(&mod, samplerate, 5500000.0, modulation * 0.1);
|
||||
mod.state = MOD_STATE_ON; /* do not ramp up */
|
||||
fm_modulate_complex(&mod, sample_tone, power_tone, samples, buff);
|
||||
}
|
||||
|
||||
double tx_frequencies[1], rx_frequencies[1];
|
||||
tx_frequencies[0] = frequency;
|
||||
rx_frequencies[0] = frequency;
|
||||
sdr = sdr_open(NULL, tx_frequencies, rx_frequencies, 1, 0.0, samplerate, latspl, 0.0, 0.0);
|
||||
if (!sdr)
|
||||
goto error;
|
||||
sdr_start(sdr);
|
||||
|
||||
int pos = 0, max = samples * 2;
|
||||
int s, ss, tosend;
|
||||
while (!quit) {
|
||||
usleep(1000);
|
||||
sdr_read(sdr, (void *)sendbuff, latspl, 0);
|
||||
tosend = sdr_get_tosend(sdr, latspl);
|
||||
if (tosend > latspl / 10)
|
||||
tosend = latspl / 10;
|
||||
if (tosend == 0) {
|
||||
continue;
|
||||
}
|
||||
for (s = 0, ss = 0; s < tosend; s++) {
|
||||
sendbuff[ss++] = buff[pos++];
|
||||
sendbuff[ss++] = buff[pos++];
|
||||
if (pos == max) {
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
sdr_write(sdr, (void *)sendbuff, NULL, tosend, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
error:
|
||||
free(sendbuff);
|
||||
free(buff);
|
||||
if (sdr)
|
||||
sdr_close(sdr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* reset signals */
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGHUP, SIG_DFL);
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
}
|
||||
|
||||
static int tx_test_picture(enum bas_type type)
|
||||
{
|
||||
bas_t bas;
|
||||
sample_t *test_bas = NULL;
|
||||
sample_t *test_tone = NULL;
|
||||
uint8_t *test_power = NULL;
|
||||
int i;
|
||||
int ret = -1;
|
||||
int count;
|
||||
|
||||
/* test image, add some samples in case of overflow due to rounding errors */
|
||||
test_bas = calloc(samplerate / 25.0 * 4.0 + 10.0, sizeof(sample_t));
|
||||
if (!test_bas) {
|
||||
fprintf(stderr, "No mem!\n");
|
||||
goto error;
|
||||
}
|
||||
bas_init(&bas, samplerate, type, fbas, circle_radius, color_bar, grid_only, station_id, NULL, 0, 0);
|
||||
count = bas_generate(&bas, test_bas);
|
||||
count += bas_generate(&bas, test_bas + count);
|
||||
count += bas_generate(&bas, test_bas + count);
|
||||
count += bas_generate(&bas, test_bas + count);
|
||||
|
||||
if (tone) {
|
||||
/* for more about audio modulation on tv, see: http://elektroniktutor.de/geraetetechnik/fston.html */
|
||||
test_tone = calloc(count, sizeof(sample_t));
|
||||
if (!test_tone) {
|
||||
fprintf(stderr, "No mem!\n");
|
||||
goto error;
|
||||
}
|
||||
test_power = calloc(count, sizeof(uint8_t));
|
||||
if (!test_power) {
|
||||
fprintf(stderr, "No mem!\n");
|
||||
goto error;
|
||||
}
|
||||
/* emphasis 50us, but 1000Hz does not change level */
|
||||
for (i = 0; i < count; i++)
|
||||
test_tone[i] = sin((double)i * 2.0 * M_PI * 1000.0 / samplerate) * 50000;
|
||||
memset(test_power, 1, count);
|
||||
}
|
||||
|
||||
tx_bas(test_bas, test_tone, test_power, count);
|
||||
|
||||
ret = 0;
|
||||
error:
|
||||
free(test_bas);
|
||||
free(test_tone);
|
||||
free(test_power);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tx_img(const char *filename)
|
||||
{
|
||||
unsigned short *img = NULL;
|
||||
int width, height;
|
||||
bas_t bas;
|
||||
sample_t *img_bas = NULL;
|
||||
int ret = -1;
|
||||
int count;
|
||||
|
||||
img = load_img(&width, &height, filename, 0);
|
||||
if (!img) {
|
||||
fprintf(stderr, "Failed to load grey image '%s'\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* test image, add some samples in case of overflow due to rounding errors */
|
||||
img_bas = calloc(samplerate / 25.0 * 4.0 + 10.0, sizeof(sample_t));
|
||||
if (!img_bas) {
|
||||
fprintf(stderr, "No mem!\n");
|
||||
goto error;
|
||||
}
|
||||
bas_init(&bas, samplerate, BAS_IMAGE, fbas, circle_radius, color_bar, grid_only, NULL, img, width, height);
|
||||
count = bas_generate(&bas, img_bas);
|
||||
count += bas_generate(&bas, img_bas + count);
|
||||
count += bas_generate(&bas, img_bas + count);
|
||||
count += bas_generate(&bas, img_bas + count);
|
||||
|
||||
tx_bas(img_bas, NULL, NULL, count);
|
||||
|
||||
ret = 0;
|
||||
error:
|
||||
free(img_bas);
|
||||
free(img);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int skip_args;
|
||||
int __attribute__((__unused__)) rc;
|
||||
const char *arg0 = argv[0];
|
||||
|
||||
debuglevel = 0;
|
||||
|
||||
#ifdef HAVE_SDR
|
||||
sdr_config_init();
|
||||
#endif
|
||||
|
||||
skip_args = handle_options(argc, argv);
|
||||
argc -= skip_args + 1;
|
||||
argv += skip_args + 1;
|
||||
|
||||
if (frequency == 0.0 && !wave_file) {
|
||||
print_help(arg0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!wave_file) {
|
||||
#ifdef HAVE_SDR
|
||||
rc = sdr_configure(samplerate);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (argc < 1) {
|
||||
fprintf(stderr, "Expecting command, see help!\n");
|
||||
exit(0);
|
||||
} else if (!strcmp(argv[0], "tx-fubk")) {
|
||||
tx_test_picture(BAS_FUBK);
|
||||
} else if (!strcmp(argv[0], "tx-vcr")) {
|
||||
tx_test_picture(BAS_VCR);
|
||||
} else if (!strcmp(argv[0], "tx-img")) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Expecting image file, see help!\n");
|
||||
exit(0);
|
||||
}
|
||||
tx_img(argv[1]);
|
||||
} else {
|
||||
fprintf(stderr, "Unknown command '%s', see help!\n", argv[0]);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/* television modulator
|
||||
*
|
||||
* (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 <math.h>
|
||||
#include <stdint.h>
|
||||
#include "../common/sample.h"
|
||||
#include "tv_modulate.h"
|
||||
|
||||
#define WHITE_MODULATION 0.1
|
||||
|
||||
void tv_modulate(float *buff, int count, sample_t *bas, double amplitude)
|
||||
{
|
||||
int i, ss = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
buff[ss++] = ((1.0 - bas[i]) * (1.0 - WHITE_MODULATION) + WHITE_MODULATION) * amplitude;
|
||||
buff[ss++] = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
void tv_modulate(float *buff, int count, sample_t *bas, double amplitude);
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
/* VCR test image generator
|
||||
*
|
||||
* (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 <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "../common/sample.h"
|
||||
#include "vcr.h"
|
||||
|
||||
/* test ID of calibration part:
|
||||
*
|
||||
* 1. line: ID of 48 Bits in 52 uS
|
||||
* 2. line: 50% Gray
|
||||
* 3. line: 50% Gray +- 25% deviation, frequency 0 Hz
|
||||
* 4. line: 50% Gray +- 12.5% deviation, frequency 0 Hz
|
||||
* 5. and 6. line: as line 3 and 4, but frequnency 0.2 MHz
|
||||
* each next two lines as above, but increments frequency by 0.2
|
||||
* 63. and 64. line: the incement reaches 6 MHz
|
||||
*/
|
||||
#define TEST_ID "VHS V1"
|
||||
|
||||
/* create cosine ramp, so that the bandwidth is not higher than 2 * width of the ramp(0..1) */
|
||||
static inline double ramp(double x)
|
||||
{
|
||||
return 0.5 - 0.5 * cos(x * M_PI);
|
||||
}
|
||||
|
||||
int vcr_gen_line(sample_t *sample, double x, double samplerate, sample_t *color_u, sample_t *color_v, int v_polarity, double line_start, double line_end, int line)
|
||||
{
|
||||
double step = 1.0 / samplerate;
|
||||
int i = 0;
|
||||
double Y, Y2, U, V, frequency, colorphase, saturation;
|
||||
double render_end, n, width;
|
||||
int b;
|
||||
|
||||
/* skip x to line_start */
|
||||
while (x < line_start && x < line_end) {
|
||||
i++;
|
||||
x += step;
|
||||
}
|
||||
if (x >= line_end)
|
||||
return i;
|
||||
|
||||
/* select test pattern */
|
||||
switch (line / 32) {
|
||||
case 0:
|
||||
case 1:
|
||||
/* frequency test
|
||||
*
|
||||
* show frequencies from 0.5 MHz to 3.5 MHz */
|
||||
frequency = (double)(line & 63) / 63.0 * 3000000.0 + 500000.0;
|
||||
while (x < line_end) {
|
||||
Y = 1.0 - (line_end - x) / (line_end - line_start);
|
||||
sample[i++] = 0.5 + Y * 0.5 * sin(x * frequency * 2 * M_PI);
|
||||
x += step;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* level test
|
||||
*
|
||||
* show levels from 0 to 100% luminance */
|
||||
Y = (double)(line & 31) / 31.0;
|
||||
while (x < line_end) {
|
||||
sample[i++] = Y;
|
||||
x += step;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/* edge test
|
||||
*
|
||||
* show edges with 0.5 MHz to 3.5 MHz frequency */
|
||||
for (n = 0.0; n < 0.99; n += 0.1) {
|
||||
frequency = (double)(line & 31) / 31.0 * 3000000.0 + 500000.0;
|
||||
width = 1.0 / frequency / 2.0; /* half wave length */
|
||||
/* low level before ramping up */
|
||||
Y = 0.5 - (n + 0.1) / 2.0;
|
||||
Y2 = 1.0 - Y;
|
||||
render_end = line_start + (line_end - line_start) / 40.0 * (40.0 * n + 1) - width / 2.0;
|
||||
while (x < render_end) {
|
||||
sample[i++] = Y;
|
||||
x += step;
|
||||
}
|
||||
/* ramp up */
|
||||
render_end += width;
|
||||
while (x < render_end) {
|
||||
sample[i++] = ramp((render_end - x) / width) * (Y - Y2) + Y2;
|
||||
x += step;
|
||||
}
|
||||
/* high level before ramping down */
|
||||
render_end = line_start + (line_end - line_start) / 40.0 * (40.0 * n + 3) - width / 2.0;
|
||||
while (x < render_end) {
|
||||
sample[i++] = Y2;
|
||||
x += step;
|
||||
}
|
||||
/* ramp down */
|
||||
render_end += width;
|
||||
while (x < render_end) {
|
||||
sample[i++] = ramp((render_end - x) / width) * (Y2 - Y) + Y;
|
||||
x += step;
|
||||
}
|
||||
/* low level after ramping down */
|
||||
render_end = line_start + (line_end - line_start) / 40.0 * (40.0 * n + 4);
|
||||
while (x < render_end) {
|
||||
sample[i++] = Y;
|
||||
x += step;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
/* color test
|
||||
*
|
||||
* show color from 0 to 100% saturation */
|
||||
Y = (1.0 - 5.0 / 7.0) * 0.75;
|
||||
saturation = (double)(line & 63) / 63.0;
|
||||
if (v_polarity < 0)
|
||||
colorphase = (360.0 - 103.5) / 180.0 * M_PI;
|
||||
else
|
||||
colorphase = 103.5 / 180.0 * M_PI;
|
||||
U = cos(colorphase) * saturation * 0.474 / 2.0;
|
||||
V = sin(colorphase) * saturation * 0.474 / 2.0;
|
||||
while (x < line_end) {
|
||||
sample[i++] = Y;
|
||||
color_u[i] = U;
|
||||
color_v[i] = V;
|
||||
x += step;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
case 7:
|
||||
/* calibration signal
|
||||
*
|
||||
* this signal is used to calibrate the frequency response of the de-emphasis
|
||||
*/
|
||||
if ((line & 63) == 0) {
|
||||
/* generate identification line to be detected by the decoder */
|
||||
for (b = 0; b < 48; b++) {
|
||||
render_end = line_start + (line_end - line_start) / 48.0 * (double)(b + 1);
|
||||
Y = (TEST_ID[b / 8] >> (b & 7)) & 1;
|
||||
while (x < render_end) {
|
||||
sample[i++] = Y;
|
||||
x += step;
|
||||
}
|
||||
}
|
||||
} else if ((line & 63) == 1) {
|
||||
/* generate zero level (50% brightness) */
|
||||
while (x < line_end) {
|
||||
sample[i++] = 0.5;
|
||||
x += step;
|
||||
}
|
||||
} else {
|
||||
/* each pair of lines: upper uses 50% deviation, lower uses 25% deviation */
|
||||
if ((line & 1) == 0)
|
||||
Y = 0.5;
|
||||
else
|
||||
Y = 0.25;
|
||||
/* frequency from 0 - 6 MHz in 31 steps (each 0.2 MHz) */
|
||||
frequency = (double)(((line & 63) - 2) / 2) * 200000.0;
|
||||
while (x < line_end) {
|
||||
sample[i++] = 0.5 + Y * 0.5 * cos(x * frequency * 2 * M_PI);
|
||||
x += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
int vcr_gen_line(sample_t *sample, double x, double samplerate, sample_t *color_u, sample_t *color_v, int v_polarity, double line_start, double line_end, int line);
|
||||
|
Loading…
Reference in New Issue