mirror of
https://github.com/jfdelnero/rf-tools.git
synced 2024-11-21 11:21:47 -05:00
Working black & white TV transmitter (PAL-L / SECAM timings and polarity by default - No Audio).
This commit is contained in:
parent
fafd7fa578
commit
03193173f3
@ -24,14 +24,17 @@ endif
|
||||
ifeq ($(findstring CYGWIN,$(shell uname)),CYGWIN)
|
||||
endif
|
||||
|
||||
EXEC=broadcast_fm rf_jammer
|
||||
EXEC=broadcast_tv broadcast_fm rf_jammer
|
||||
|
||||
all: $(EXEC)
|
||||
|
||||
broadcast_fm: broadcast_fm.o rds.o rds_stream_dump.o wave.o modulator.o FIR_Audio_Filter_Filter.o FM_Baseband_Filter.o AudioPreemphasis_Filter.o FIR_RDS_Passband_Filter.o hxcmod.o rand_gen.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
rf_jammer: rf_jammer.o wave.o modulator.o rand_gen.o
|
||||
broadcast_tv: broadcast_tv.o composite.o wave.o modulator.o bmp_file.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
rf_jammer: rf_jammer.o wave.o modulator.o rand_gen.o composite.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
broadcast_fm.o: ../src/broadcast_fm/broadcast_fm.c
|
||||
@ -70,9 +73,18 @@ AudioPreemphasis_Filter.o: ../src/broadcast_fm/fir_filters/AudioPreemphasis_Filt
|
||||
hxcmod.o: ../src/common/hxcmod/hxcmod.c
|
||||
$(CC) -o $@ -c $< $(CFLAGS)
|
||||
|
||||
bmp_file.o: ../src/common/bmp_file.c
|
||||
$(CC) -o $@ -c $< $(CFLAGS)
|
||||
|
||||
rand_gen.o: ../src/common/rand_gen.c
|
||||
$(CC) -o $@ -c $< $(CFLAGS)
|
||||
|
||||
composite.o: ../src/broadcast_tv/composite.c
|
||||
$(CC) -o $@ -c $< $(CFLAGS)
|
||||
|
||||
broadcast_tv.o: ../src/broadcast_tv/broadcast_tv.c
|
||||
$(CC) -o $@ -c $< $(CFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf *.o
|
||||
rm -rf *.so
|
||||
|
255
src/broadcast_tv/broadcast_tv.c
Normal file
255
src/broadcast_tv/broadcast_tv.c
Normal file
@ -0,0 +1,255 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
//-------------------------------------------------------------------------------//
|
||||
//-------------------------------------------------------------------------------//
|
||||
//-----------H----H--X----X-----CCCCC----22222----0000-----0000------11----------//
|
||||
//----------H----H----X-X-----C--------------2---0----0---0----0--1--1-----------//
|
||||
//---------HHHHHH-----X------C----------22222---0----0---0----0-----1------------//
|
||||
//--------H----H----X--X----C----------2-------0----0---0----0-----1-------------//
|
||||
//-------H----H---X-----X---CCCCC-----222222----0000-----0000----1111------------//
|
||||
//-------------------------------------------------------------------------------//
|
||||
//----------------------------------------------------- http://hxc2001.free.fr --//
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// File : broadcast_tv.c
|
||||
// Contains: a broadcast TV modulator
|
||||
//
|
||||
// This file is part of rf-tools.
|
||||
//
|
||||
// Written by: Jean-François DEL NERO
|
||||
//
|
||||
// Copyright (C) 2023 Jean-François DEL NERO
|
||||
//
|
||||
// You are free to do what you want with this code.
|
||||
// A credit is always appreciated if you use it into your product :)
|
||||
//
|
||||
// Change History (most recent first):
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Disclaimer / Legal warning : Radio spectrum and the law
|
||||
//
|
||||
// In most countries the use of any radio transmitting device is required to be
|
||||
// either licensed or specifically exempted from licensing under the local regulator.
|
||||
// Other than as used in accordance with a licence (or exemption),
|
||||
// the use of radio equipment is illegal.
|
||||
//
|
||||
// So take care to limit the emitting range and power when testing this software !
|
||||
//
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "wave.h"
|
||||
|
||||
#include "modulator.h"
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "composite.h"
|
||||
|
||||
#include "bmp_file.h"
|
||||
|
||||
#define IQ_SAMPLE_RATE 16000000
|
||||
|
||||
#define IF_FREQ 0
|
||||
|
||||
#define BUFFER_SAMPLES_SIZE (1024*8)
|
||||
|
||||
#define printf(fmt...) do { \
|
||||
if(!stdoutmode) \
|
||||
fprintf(stdout, fmt); \
|
||||
} while (0)
|
||||
|
||||
int stdoutmode;
|
||||
|
||||
int isOption(int argc, char* argv[],char * paramtosearch,char * argtoparam)
|
||||
{
|
||||
int param=1;
|
||||
int i,j;
|
||||
|
||||
char option[512];
|
||||
|
||||
memset(option,0,512);
|
||||
while(param<=argc)
|
||||
{
|
||||
if(argv[param])
|
||||
{
|
||||
if(argv[param][0]=='-')
|
||||
{
|
||||
memset(option,0,512);
|
||||
|
||||
j=0;
|
||||
i=1;
|
||||
while( argv[param][i] && argv[param][i]!=':')
|
||||
{
|
||||
option[j]=argv[param][i];
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
if( !strcmp(option,paramtosearch) )
|
||||
{
|
||||
if(argtoparam)
|
||||
{
|
||||
if(argv[param][i]==':')
|
||||
{
|
||||
i++;
|
||||
j=0;
|
||||
while( argv[param][i] )
|
||||
{
|
||||
argtoparam[j]=argv[param][i];
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
argtoparam[j]=0;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
param++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void printhelp(char* argv[])
|
||||
{
|
||||
printf("Options:\n");
|
||||
printf(" -stdout \t\t\t: IQ stream send to stdout\n");
|
||||
printf(" -bmp_file:[BMPFILE.BMP]\t: BMP image file to display\n");
|
||||
printf(" -generate \t\t\t: Generate the video IQ stream\n");
|
||||
printf(" -help \t\t\t: This help\n\n");
|
||||
printf("Example : 525MHz TV broadcasting with an hackrf (currently B&W with PAL-L/SECAM timings and polarity)\n./broadcast_tv -generate -stdout -bmp_file:Philips_PM5544.bmp | hackrf_transfer -f 525000000 -t - -x 47 -a 1 -s 16000000\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
unsigned int i,j;
|
||||
|
||||
int ret;
|
||||
char filename[512];
|
||||
|
||||
wave_io * wave1,*wave2;
|
||||
|
||||
double video_buf[BUFFER_SAMPLES_SIZE];
|
||||
composite_state vid_stat;
|
||||
int16_t subcarriers_dbg_wave_buf[BUFFER_SAMPLES_SIZE];
|
||||
uint16_t iq_wave_buf[ BUFFER_SAMPLES_SIZE];
|
||||
|
||||
iq_wave_gen iqgen;
|
||||
bitmap_data bmp_data;
|
||||
|
||||
stdoutmode = 0;
|
||||
|
||||
if(isOption(argc,argv,"stdout",NULL)>0)
|
||||
{
|
||||
stdoutmode = 1;
|
||||
}
|
||||
|
||||
if(!stdoutmode)
|
||||
{
|
||||
printf("broadcast_tv v0.0.1.1\n");
|
||||
printf("Copyright (C) 2023 Jean-Francois DEL NERO\n");
|
||||
printf("This program comes with ABSOLUTELY NO WARRANTY\n");
|
||||
printf("This is free software, and you are welcome to redistribute it\n");
|
||||
printf("under certain conditions;\n\n");
|
||||
}
|
||||
|
||||
// help option...
|
||||
if(isOption(argc,argv,"help",0)>0)
|
||||
{
|
||||
printhelp(argv);
|
||||
}
|
||||
|
||||
memset(filename,0,sizeof(filename));
|
||||
|
||||
if(isOption(argc,argv,"bmp_file",(char*)&filename)>0)
|
||||
{
|
||||
printf("Input file : %s\n",filename);
|
||||
}
|
||||
|
||||
memset(&bmp_data,0,sizeof(bmp_data));
|
||||
if(strlen(filename))
|
||||
{
|
||||
ret = bmp_load(filename,&bmp_data);
|
||||
if( ret >= 0)
|
||||
{
|
||||
printf("BMP file loaded successfully\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("BMP load error %d\n",ret);
|
||||
}
|
||||
}
|
||||
|
||||
if(isOption(argc,argv,"generate",0)>0)
|
||||
{
|
||||
// IQ Modulator
|
||||
iqgen.phase = 0;
|
||||
iqgen.Frequency = IF_FREQ;
|
||||
iqgen.Amplitude = 127;
|
||||
iqgen.sample_rate = IQ_SAMPLE_RATE;
|
||||
|
||||
init_composite(&vid_stat, 16000000, 768, 576,bmp_data.data);
|
||||
|
||||
if(stdoutmode)
|
||||
{
|
||||
// stdout / stream mode : IQ are outputed to the stdout -> use a pipe to hackrf_transfer
|
||||
wave1 = create_wave(NULL,iqgen.sample_rate,WAVE_FILE_FORMAT_RAW_8BITS_IQ);
|
||||
wave2 = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// file mode : create iq + wav files
|
||||
wave1 = create_wave("broadcast_tv.iq",iqgen.sample_rate,WAVE_FILE_FORMAT_RAW_8BITS_IQ);
|
||||
wave2 = create_wave("broadcast_tv.wav",16000000,WAVE_FILE_FORMAT_WAV_16BITS_MONO);
|
||||
}
|
||||
|
||||
for(i=0;i<1024*16*2 || stdoutmode;i++)
|
||||
{
|
||||
gen_video_signal(&vid_stat, (double*)&video_buf, BUFFER_SAMPLES_SIZE);
|
||||
for(j=0;j<BUFFER_SAMPLES_SIZE;j++)
|
||||
{
|
||||
subcarriers_dbg_wave_buf[j] = (int)((video_buf[j]/(double)100) * (double)32767);
|
||||
|
||||
iqgen.Amplitude = ((double)video_buf[j]);
|
||||
iq_wave_buf[j] = get_next_iq(&iqgen);
|
||||
}
|
||||
|
||||
write_wave(wave1, &iq_wave_buf,BUFFER_SAMPLES_SIZE);
|
||||
write_wave(wave2, &subcarriers_dbg_wave_buf,BUFFER_SAMPLES_SIZE);
|
||||
}
|
||||
|
||||
close_wave(wave1);
|
||||
close_wave(wave2);
|
||||
}
|
||||
|
||||
if( (isOption(argc,argv,"help",0)<=0) &&
|
||||
(isOption(argc,argv,"generate",0)<=0)
|
||||
)
|
||||
{
|
||||
printhelp(argv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -32,10 +32,14 @@
|
||||
|
||||
#define EQUALIZING_PULSE_LEN 2.35
|
||||
#define LINE_PULSE_LEN 4.7
|
||||
#define ASSERTED_LEVEL 10
|
||||
#define DEASSERTED_LEVEL 100
|
||||
#define ASSERTED_LEVEL 0
|
||||
#define DEASSERTED_LEVEL 40
|
||||
#define COMPOSITE_LINE_PERIOD_US 64
|
||||
|
||||
// PAL timings
|
||||
// PAL / Secam : 768 x 576
|
||||
// NTSC : 720 x 480 (Old : 640 x 480)
|
||||
|
||||
// PAL-L timings
|
||||
pulses_state vertical_blanking[]=
|
||||
{
|
||||
// First sequence of equalizing pulses
|
||||
@ -45,8 +49,11 @@ pulses_state vertical_blanking[]=
|
||||
// Duration of second sequence of equalizing pulses
|
||||
{ 5, ASSERTED_LEVEL, DEASSERTED_LEVEL, EQUALIZING_PULSE_LEN/(double)1E6 ,((COMPOSITE_LINE_PERIOD_US/2)/(double)1E6),0},
|
||||
|
||||
// Lines (6 > 310)
|
||||
{305, ASSERTED_LEVEL, DEASSERTED_LEVEL, LINE_PULSE_LEN/(double)1E6 ,((COMPOSITE_LINE_PERIOD_US)/(double)1E6),0},
|
||||
// Blank Lines (6 > 23)
|
||||
{18, ASSERTED_LEVEL, DEASSERTED_LEVEL, LINE_PULSE_LEN/(double)1E6 ,((COMPOSITE_LINE_PERIOD_US)/(double)1E6),0},
|
||||
|
||||
// Lines (24 > 310)
|
||||
{287, ASSERTED_LEVEL, DEASSERTED_LEVEL, LINE_PULSE_LEN/(double)1E6 ,((COMPOSITE_LINE_PERIOD_US)/(double)1E6),1},
|
||||
|
||||
// First sequence of equalizing pulses
|
||||
{ 5, ASSERTED_LEVEL, DEASSERTED_LEVEL, EQUALIZING_PULSE_LEN/(double)1E6 ,((COMPOSITE_LINE_PERIOD_US/2)/(double)1E6),0},
|
||||
@ -58,8 +65,11 @@ pulses_state vertical_blanking[]=
|
||||
// -- end of line 318
|
||||
{ 1, DEASSERTED_LEVEL, DEASSERTED_LEVEL, 0 ,(COMPOSITE_LINE_PERIOD_US/2)/(double)1E6,0},
|
||||
|
||||
// Lines (319 > 622)
|
||||
{304, ASSERTED_LEVEL, DEASSERTED_LEVEL, LINE_PULSE_LEN/(double)1E6 ,(COMPOSITE_LINE_PERIOD_US)/(double)1E6,0},
|
||||
// Blank Lines (319 > 335)
|
||||
{17, ASSERTED_LEVEL, DEASSERTED_LEVEL, LINE_PULSE_LEN/(double)1E6 ,(COMPOSITE_LINE_PERIOD_US)/(double)1E6,0},
|
||||
|
||||
// Lines (336 > 622)
|
||||
{287, ASSERTED_LEVEL, DEASSERTED_LEVEL, LINE_PULSE_LEN/(double)1E6 ,(COMPOSITE_LINE_PERIOD_US)/(double)1E6,3},
|
||||
|
||||
// Line 623 - (half)
|
||||
{ 1, ASSERTED_LEVEL, DEASSERTED_LEVEL, LINE_PULSE_LEN/(double)1E6 ,((COMPOSITE_LINE_PERIOD_US)/2)/(double)1E6,0},
|
||||
@ -69,24 +79,65 @@ pulses_state vertical_blanking[]=
|
||||
};
|
||||
|
||||
|
||||
void init_composite(composite_state * state, int sample_rate, int x_res, int y_res)
|
||||
void init_composite(composite_state * state, int sample_rate, int x_res, int y_res, uint32_t * bmp)
|
||||
{
|
||||
int i;
|
||||
state->sample_period = (double)1 / (double)sample_rate;
|
||||
|
||||
state->buf_x_res = x_res;
|
||||
state->buf_y_res = y_res;
|
||||
|
||||
state->video_buffer = bmp;
|
||||
|
||||
if(!state->video_buffer)
|
||||
{
|
||||
state->video_buffer = malloc( x_res * y_res * sizeof(uint32_t) );
|
||||
if(state->video_buffer)
|
||||
{
|
||||
memset(state->video_buffer,0,x_res * y_res * sizeof(uint32_t) );
|
||||
|
||||
// Basic Mire test init
|
||||
|
||||
for(i=0;i<x_res;i++)
|
||||
state->video_buffer[i] = 0xFFFFFF;
|
||||
|
||||
for(i=0;i<x_res;i++)
|
||||
state->video_buffer[((y_res-1)*x_res) + i] = 0xFFFFFF;
|
||||
|
||||
for(i=0;i<y_res;i++)
|
||||
state->video_buffer[((i)*x_res)] = 0xFFFFFF;
|
||||
|
||||
for(i=0;i<y_res;i++)
|
||||
state->video_buffer[((i)*x_res) + (x_res - 1)] = 0xFFFFFF;
|
||||
|
||||
for(i=0;i<x_res/2;i++)
|
||||
{
|
||||
state->video_buffer[(x_res/4) + (((y_res/4)-1)*x_res) + i] = 0xFFFFFF;
|
||||
state->video_buffer[(x_res/4) + (((y_res/4)-1)*x_res) + i + x_res] = 0xFFFFFF;
|
||||
|
||||
state->video_buffer[(x_res/4) + (((y_res - (y_res/4))-1)*x_res) + i] = 0xFFFFFF;
|
||||
state->video_buffer[(x_res/4) + (((y_res - (y_res/4))-1)*x_res) + i + x_res] = 0xFFFFFF;
|
||||
|
||||
state->video_buffer[(x_res/4) + (((y_res - (y_res/2))-1)*x_res) + i] = 0x6F6F6F;
|
||||
state->video_buffer[(x_res/4) + (((y_res - (y_res/2))-1)*x_res) + i + x_res] = 0x6F6F6F;
|
||||
}
|
||||
|
||||
for(i=y_res/4;i<(y_res - (y_res/4));i++)
|
||||
{
|
||||
state->video_buffer[(x_res/4) + ((i)*x_res)] = 0xFFFFFF;
|
||||
state->video_buffer[(x_res/4) + ((i)*x_res) + (x_res/4)] = 0x6F6F6F;
|
||||
state->video_buffer[(x_res/2) + ((i)*x_res) + (x_res/4)] = 0xFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state->step_index = 0;
|
||||
state->cur_state_time = 0;
|
||||
}
|
||||
|
||||
void gen_video_signal(composite_state * state, double * vid_signal, int buf_size)
|
||||
{
|
||||
int i;
|
||||
int i,xpos;
|
||||
double value;
|
||||
|
||||
value = 0;
|
||||
@ -98,6 +149,11 @@ void gen_video_signal(composite_state * state, double * vid_signal, int buf_size
|
||||
state->cur_state_time = 0;
|
||||
state->repeat_cnt++;
|
||||
|
||||
if(vertical_blanking[state->step_index].type & 1)
|
||||
state->cur_line_index++;
|
||||
else
|
||||
state->cur_line_index = 0;
|
||||
|
||||
if( state->repeat_cnt >= vertical_blanking[state->step_index].repeat )
|
||||
{
|
||||
state->repeat_cnt = 0;
|
||||
@ -119,6 +175,35 @@ void gen_video_signal(composite_state * state, double * vid_signal, int buf_size
|
||||
value = vertical_blanking[state->step_index].start_val;
|
||||
}
|
||||
|
||||
if(vertical_blanking[state->step_index].type & 1)
|
||||
{
|
||||
if(state->cur_state_time >= vertical_blanking[state->step_index].first_duration)
|
||||
{
|
||||
if(state->cur_state_time >= vertical_blanking[state->step_index].first_duration + ((3.5+4.7)/(double)1E6))
|
||||
{
|
||||
// Stream pixels line
|
||||
if(state->cur_state_time <= vertical_blanking[state->step_index].first_duration + ((3.5+4.7)/(double)1E6) + (45.6/(double)1E6) )
|
||||
{
|
||||
xpos = ((state->cur_state_time - ( vertical_blanking[state->step_index].first_duration + ((3.5+4.7)/(double)1E6) )) / (45.6/(double)1E6)) * state->buf_x_res;
|
||||
|
||||
value = vertical_blanking[state->step_index].end_val;
|
||||
value += ((double)((state->video_buffer[(state->cur_line_index * state->buf_x_res * 2) + ( state->buf_x_res * (((vertical_blanking[state->step_index].type>>1)^1)&1)) + xpos] & 0xFF)) * ((double)((double)100.0 - vertical_blanking[state->step_index].end_val)/(double)256));
|
||||
}
|
||||
else
|
||||
{
|
||||
value = vertical_blanking[state->step_index].end_val;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO : Color Burst
|
||||
value = vertical_blanking[state->step_index].end_val;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
state->cur_line_index = 0;
|
||||
|
||||
vid_signal[i] = value;
|
||||
|
||||
state->cur_state_time += state->sample_period;
|
||||
|
@ -24,8 +24,6 @@
|
||||
// Change History (most recent first):
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define COMPOSITE_LINE_PERIOD_US 64.0
|
||||
|
||||
typedef struct composite_state_
|
||||
{
|
||||
double sample_period;
|
||||
@ -35,6 +33,7 @@ typedef struct composite_state_
|
||||
|
||||
uint32_t * video_buffer;
|
||||
|
||||
int cur_line_index;
|
||||
int step_index;
|
||||
int repeat_cnt;
|
||||
|
||||
@ -53,5 +52,5 @@ typedef struct pulses_state_
|
||||
}pulses_state;
|
||||
|
||||
|
||||
void init_composite(composite_state * state, int sample_rate, int x_res, int y_res);
|
||||
void init_composite(composite_state * state, int sample_rate, int x_res, int y_res, uint32_t * bmp);
|
||||
void gen_video_signal(composite_state * state, double * vid_signal, int buf_size);
|
||||
|
Loading…
Reference in New Issue
Block a user