UKHAS Wiki

UK High Altitude Society

User Tools

Site Tools


code:sstvmod

SSTV code by Fabian Kurz, DJ1YFK - improved speed version by Steve Randall

/*
 * sstvtx - Martin 1 mode SSTV generator
 *
 * $Id: sstvtx.c 82 2008-07-30 12:13:59Z dj1yfk $
 *
 * Copyright (c) 2008 Fabian Kurz, DJ1YFK
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */
 
/*
 * Hacked by Steve Randall to speed audio file generation up (and bug fix)
 * Compile with: gcc sstvtxx.c -lsndfile -ljpeg -lrt -lm -o sstvtx
 */
 
 
 
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sndfile.h>
#include "jpeglib.h"
#include "jerror.h"
#include <setjmp.h>
#include <unistd.h>
 
#include <time.h> // timing profile debug

struct timespec tp[8]; // timing profile debug
int clock_gettime(clockid_t clk_id, struct timespec *tp);

#define PIXEL 1
#define SYNC 0

#define SYNC_FREQ	1200.0
#define BLACK_FREQ	1500.0
#define WHITE_FREQ	2300.0

#define OUT_LEVEL	25000.0 // peak sound level

#define TWO_PI		6.28318530717958647693

#define SAMPLE_RATE	48000 // sound file sanples per sec
#define PIX_WIDTH	320
#define PIX_HEIGHT	256
 
/* JPEG input image will be read into this array; picture size 320x256,
 * RGB components in separate rows */
static unsigned char pic[PIX_WIDTH][PIX_HEIGHT * 3];	
 
typedef struct {
	int samplerate;
	double sync_len;	// length of synch impulse in sec 
	double pix_len;		// length of normal pixel in sec
	int x_width;
	double end_phase;	// ending phase of pixel
	double next_time;	// time (in sec) to next sample
	char *infile;
	char *outfile;
} SSTV;
 
void die(char *err);
int read_JPEG_file (char *filename);
int tx_sync(SNDFILE *snd, SSTV *sstv);
int tx_pixel(unsigned char p, SNDFILE *snd, SSTV *sstv, int typ);
void help (void);

long get_ms(int os)
{
	long ta;
	long tb;

	ta = tp[os].tv_sec * 1000l; // convert secs to ms;
	ta += (tp[os].tv_nsec) / 1000000l; // add in usec converted to ms
	os++;
	tb = tp[os].tv_sec * 1000l; // convert secs to ms;
	tb += (tp[os].tv_nsec) / 1000000l; // add in usec converted to ms

	return(tb - ta);
}

void dump_times(void)
{
	fprintf(stderr,"time 0 to 1 %ld ms\n",get_ms(0));
	fprintf(stderr,"time 1 to 2 %ld ms\n",get_ms(1));
	fprintf(stderr,"time 2 to 3 %ld ms\n",get_ms(2));
}

 
int main (int argc, char **argv) {
	int x,y;
 
	SSTV sstv;
	SNDFILE *out;
	SF_INFO out_info;
 
	sstv.samplerate = SAMPLE_RATE;
	sstv.x_width = PIX_WIDTH;
	/* total pixels = 320 * 256 * 3 = 245760
	   total sync = 256 * 5ms = 1.28 sec
	   pixel length, in samples, total pixels 245760, 114s - 1.28s sync 
	   results in 0.45865885... ms / pixel */
	sstv.pix_len = 0.00045865885416666666;
	sstv.sync_len = 0.005; // sync pulse length 5ms
	sstv.end_phase = 0.0;
	sstv.next_time = 1.0 / (double)SAMPLE_RATE;
	sstv.outfile = "out.wav";
	sstv.infile = "input.jpg";

 clock_gettime(CLOCK_REALTIME, &tp[0]);
 
	while ((x = getopt(argc, argv, "s:i:o:x:h")) != -1) {
		switch (x) {
			case 's':
				sstv.samplerate = atoi(optarg);
				break;
			case 'i':
				sstv.infile = optarg;
				break;
			case 'o':
				sstv.outfile = optarg;
				break;
			case 'h':
				help();
				break;
			case 'x':
				sstv.x_width= atoi(optarg);
				break;
		}
	}
 
	out_info.samplerate = sstv.samplerate;
	out_info.channels = 1;
	out_info.format = SF_FORMAT_WAV|SF_FORMAT_PCM_16;
 
	if ((out = sf_open(sstv.outfile, SFM_WRITE, &out_info)) == NULL) {
		die("Opening soundfile failed.");
	}

 clock_gettime(CLOCK_REALTIME, &tp[1]);
 
	read_JPEG_file(sstv.infile);

 clock_gettime(CLOCK_REALTIME, &tp[2]);
 
	for (y=0; y < (PIX_HEIGHT * 3); y++) 
	{
		for (x = 0; x < sstv.x_width; x++) 
		{ // generate the wav file
			tx_pixel(pic[x][y], out, &sstv, PIXEL);
		}
		if (!(y % 3)) 
		{ // insert sync pulse after line generated
			tx_pixel(0, out, &sstv, SYNC);
		}
	}

 clock_gettime(CLOCK_REALTIME, &tp[3]);
 
	sf_close(out);

dump_times();
 
return 0;
}
 
void die(char *err) {
	fprintf(stderr, "Error: %s\n", err);
	exit(EXIT_FAILURE);
}
 
int read_JPEG_file (char *filename) {
	struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	FILE *infile;
	JSAMPARRAY buffer;
	int row_stride;
	int i, row=0;
 
	if ((infile = fopen(filename, "rb")) == NULL) {
		die("Can't open input file.");
	}
 
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	jpeg_stdio_src(&cinfo, infile);
	(void) jpeg_read_header(&cinfo, TRUE);
	(void) jpeg_start_decompress(&cinfo);
 
	if (cinfo.output_width != PIX_WIDTH || cinfo.output_height != PIX_HEIGHT) {
		die ("Input file format wrong. Need 320x256.");
	}
 
	row_stride = cinfo.output_width * cinfo.output_components;
 
	buffer = (*cinfo.mem->alloc_sarray)
			((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
 
	while (cinfo.output_scanline < cinfo.output_height) {
		(void) jpeg_read_scanlines(&cinfo, buffer, 1);
 
		/* each line in buffer: R,G,B,R,G,B -> put to array RRR/GGG/BBB */
		for (i=0; i < cinfo.output_width; i++) {
			pic[i][row] = buffer[0][3*i];
			pic[i][row+1] = buffer[0][3*i+1];
			pic[i][row+2] = buffer[0][3*i+2];
		}
		row+=3;
	}
 
	(void) jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);
 
	fclose(infile);
 
	return 0;
}
  
// this routines generates the sound samples representing a single pixel
// p contains the pixel intensity 

int tx_pixel(unsigned char p, SNDFILE *snd, SSTV *sstv, int typ) {
	double freq;		// claculated frequency of sample (in rads/sec)			
	short int buf[500];	// buffer for audio samples
	int j;			// count of samples created
	double len;		// duration of sample to be created
	double end_phase;	// phase at sample end
	double ph_inc;		// phase increment (between audio samples)
	double phase;		// current phase
 
	// claculate the frequency associated with the pixel
	// normalize char to frequency range of 1500-2300Hz
	if (typ == PIXEL) {
		freq = TWO_PI * (BLACK_FREQ + (((WHITE_FREQ - BLACK_FREQ) * (double)p)/255.0));
		len = sstv->pix_len;
	}
	else {
		freq = TWO_PI * SYNC_FREQ;
		len = sstv->sync_len;
	}
 
	// calculate the phase at the end of the pixel/sync
	end_phase = sstv->end_phase + (freq * len); 

	// calculate phase increment between audio samples
	ph_inc = (freq / sstv->samplerate);

	// calculate phase at initial sample in pixel/sync
	phase = sstv->end_phase + (freq) * sstv->next_time;

	j = 0;
	while (phase < end_phase)
	{
		buf[j++] = (short int) (OUT_LEVEL * sin(phase));
		phase += ph_inc;
	}

	// claculate time next sample is beyond pixel/sync end
	sstv->next_time = (phase - end_phase) / (freq);

	// loop exits with j containing the number of samples written 
 
	while (end_phase > TWO_PI) { // phase can increment several cycles during sync
		end_phase -= TWO_PI; // keep in reasonable bounds
	}
	sstv->end_phase = end_phase; // new end_phase 

	if (sf_write_short(snd, buf, j) != j) {
		die("Writing Pixel failed.");
	}
	return 0;
}
 
 
void help (void) {
	printf("sstvtx - generates a SSTV Martin 1 image\n");
	printf("\n");
	printf("Usage: sstvtx -i input.jpg -o output.wav -x x_width -s samplerate\n");
	printf("\n");
	printf("Written by Fabian Kurz, DJ1YFK; http://fkurz.net/ham/stuff.html#sstvtx\n\n");
	exit(EXIT_SUCCESS);
}

code/sstvmod.txt · Last modified: 2008/08/22 21:31 by rocketboy

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki