====== 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 #include #include #include #include "jpeglib.h" #include "jerror.h" #include #include #include // 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); }