UKHAS Wiki

UK High Altitude Society

User Tools

Site Tools


code:sstvtimed

SSTV code by Fabian Kurz, DJ1YFK - Hacked by Steve Randall to add timing points

/*
 * 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 version by Steve Randall - added timing points
 * Compile with: gcc sstvtxw.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
 
/* JPEG input image will be read into this array; picture size 320x256,
 * RGB components in separate rows */
static unsigned char pic[320][3*256];	
 
typedef struct {
	int samplerate;
	int sync_len;		/* length of synch impulse in samples */
	int pix_len;		/* length of normal pixel in samples */
	int x_width;
	double phase;
	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);
int init_sstv(SSTV *sstv);
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 = 48000;
	sstv.x_width = 320;
	sstv.phase = 0;
	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]);
 
	init_sstv(&sstv);
 
	for (y=0; y < 3*256; 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 != 320 || cinfo.output_height != 256) {
		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;
}
 
int init_sstv(SSTV *sstv) {
	/* sync len = 5ms */
	sstv->sync_len = sstv->samplerate * 0.005;
 
	/* pixel length, in samples, total pixels 245760, 114s - 1.28s sync 
	 * results in 0.45865885 ms / pixel */
 
	sstv->pix_len = (int) sstv->samplerate * 0.00045865885416666666;
 
	return 0;
}
 
 
int tx_pixel(unsigned char p, SNDFILE *snd, SSTV *sstv, int typ) {
	float freq;
	short int buf[500];
	int j, len;
        float i=0.0;
	double phase=0;
 
	/* normalize char to frequency range of 1500-2300Hz */
	if (typ == PIXEL) {
		freq = BLACK_FREQ + (((WHITE_FREQ - BLACK_FREQ) * (float)p)/255.0);
		len = sstv->pix_len;
	}
	else {
		freq = SYNC_FREQ;
		len = sstv->sync_len;
	}
 
	/* find back to old phase */
/*	while (phase < sstv->phase) {
		i++;
		phase = 2.0*M_PI*freq*i/sstv->samplerate;
	}
*/
        i = sstv->phase * sstv->samplerate / (2.0*M_PI*freq);
 
	/* make pixel, starting with that phase */
	for (j=0; j < len; j++) {
		phase = 2.0*M_PI*freq*i/sstv->samplerate;
		i++;
		buf[j] = (short int) 25000 * sin(phase);
	}
 
	if (phase > 2*M_PI) {
		phase -= 2*M_PI;
	}
 
	sstv->phase = phase;
 
	if ((i = 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/sstvtimed.txt ยท Last modified: 2008/08/22 21:28 by rocketboy