2011-07-07 14:09:57 +00:00
|
|
|
/*
|
|
|
|
* slowrx - an SSTV decoder
|
|
|
|
* * * * * * * * * * * * * *
|
2011-07-07 14:14:50 +00:00
|
|
|
*
|
2011-07-07 14:09:57 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2011-08-20 11:49:29 +00:00
|
|
|
#include <stdbool.h>
|
2011-07-07 14:09:57 +00:00
|
|
|
#include <math.h>
|
|
|
|
#include <time.h>
|
2012-12-24 00:31:40 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include <pnglite.h>
|
2011-08-20 05:51:27 +00:00
|
|
|
#include <pthread.h>
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-07-29 20:09:42 +00:00
|
|
|
#include <alsa/asoundlib.h>
|
|
|
|
|
2012-12-25 20:43:26 +00:00
|
|
|
#ifdef GPL
|
|
|
|
#include <fftw3.h>
|
|
|
|
#endif
|
|
|
|
|
2011-07-07 14:09:57 +00:00
|
|
|
#include "common.h"
|
|
|
|
|
2012-12-24 00:31:40 +00:00
|
|
|
void ensure_dir_exists(const char *dir) {
|
|
|
|
struct stat buf;
|
|
|
|
|
|
|
|
int i = stat(dir, &buf);
|
|
|
|
if (i != 0) {
|
|
|
|
if (mkdir(dir, 0777) != 0) {
|
|
|
|
perror("Unable to create directory for output file");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-07 17:46:35 +00:00
|
|
|
void *Listen() {
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
int Skip = 0;
|
|
|
|
char timestr[40], pngfilename[40], lumfilename[40], infostr[60], rctime[8];
|
|
|
|
guchar *pixels, Mode=0;
|
|
|
|
double Rate;
|
|
|
|
struct tm *timeptr = NULL;
|
|
|
|
time_t timet;
|
|
|
|
FILE *LumFile;
|
2011-08-20 11:49:29 +00:00
|
|
|
bool Finished;
|
2011-08-18 00:04:28 +00:00
|
|
|
GdkPixbuf *thumbbuf;
|
|
|
|
char id[20];
|
2011-08-13 12:26:49 +00:00
|
|
|
GtkTreeIter iter;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-16 21:29:38 +00:00
|
|
|
/** Prepare FFT **/
|
|
|
|
in = fftw_malloc(sizeof(double) * 2048);
|
|
|
|
if (in == NULL) {
|
|
|
|
perror("GetVideo: Unable to allocate memory for FFT");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
out = fftw_malloc(sizeof(double) * 2048);
|
|
|
|
if (out == NULL) {
|
|
|
|
perror("GetVideo: Unable to allocate memory for FFT");
|
|
|
|
fftw_free(in);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2011-08-18 00:04:28 +00:00
|
|
|
memset(in, 0, sizeof(double) * 2048);
|
|
|
|
memset(out, 0, sizeof(double) * 2048);
|
2011-08-16 21:29:38 +00:00
|
|
|
|
|
|
|
Plan1024 = fftw_plan_r2r_1d(1024, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
|
|
|
|
Plan2048 = fftw_plan_r2r_1d(2048, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
|
|
|
|
|
|
|
|
|
2011-08-20 11:49:29 +00:00
|
|
|
while (true) {
|
2011-07-25 11:16:48 +00:00
|
|
|
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_enter ();
|
2011-08-20 11:49:29 +00:00
|
|
|
gtk_widget_set_sensitive (vugrid, true);
|
|
|
|
gtk_widget_set_sensitive (btnabort, false);
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_leave ();
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-16 21:29:38 +00:00
|
|
|
HedrShift = 0;
|
|
|
|
PcmPointer = 0;
|
|
|
|
Rate = 44100;
|
2011-08-08 21:36:47 +00:00
|
|
|
snd_pcm_prepare(pcm_handle);
|
|
|
|
snd_pcm_start (pcm_handle);
|
2011-08-13 12:26:49 +00:00
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
// Wait for VIS
|
2011-07-07 14:09:57 +00:00
|
|
|
Mode = GetVIS();
|
2011-08-13 12:26:49 +00:00
|
|
|
|
2011-08-11 11:48:35 +00:00
|
|
|
if (Mode == 0) exit(EXIT_FAILURE);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
printf(" ==== %s ====\n", ModeSpec[Mode].Name);
|
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
// Store time of reception
|
2011-07-07 14:09:57 +00:00
|
|
|
timet = time(NULL);
|
|
|
|
timeptr = gmtime(&timet);
|
2011-08-14 16:34:14 +00:00
|
|
|
strftime(timestr, sizeof(timestr)-1,"%Y%m%d-%H%M%Sz", timeptr);
|
|
|
|
snprintf(pngfilename, sizeof(timestr)-1, "rx/%s_%s.png", timestr, ModeSpec[Mode].ShortName);
|
2012-04-06 12:38:41 +00:00
|
|
|
snprintf(lumfilename, sizeof(timestr)-1, "rx-lum/%s_%s-lum", timestr, ModeSpec[Mode].ShortName);
|
2011-07-07 14:09:57 +00:00
|
|
|
printf(" \"%s\"\n", pngfilename);
|
|
|
|
|
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
// Allocate space for cached Lum
|
|
|
|
StoredLum = calloc( (int)(ModeSpec[Mode].LineLen * ModeSpec[Mode].ImgHeight + 1) * 44100, sizeof(guchar));
|
|
|
|
if (StoredLum == NULL) {
|
|
|
|
perror("Listen: Unable to allocate memory for Lum");
|
2011-07-07 14:09:57 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2011-08-12 20:06:23 +00:00
|
|
|
|
|
|
|
// Allocate space for sync signal
|
2011-08-20 11:49:29 +00:00
|
|
|
HasSync = calloc((int)(ModeSpec[Mode].LineLen * ModeSpec[Mode].ImgHeight / 1.5e-3 +1), sizeof(bool));
|
2011-08-12 20:06:23 +00:00
|
|
|
if (HasSync == NULL) {
|
2011-08-18 00:04:28 +00:00
|
|
|
perror("Listen: Unable to allocate memory for sync signal");
|
2011-08-12 20:06:23 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
// Get video
|
2011-08-13 12:26:49 +00:00
|
|
|
strftime(rctime, sizeof(rctime)-1, "%H:%M", timeptr);
|
2011-08-10 20:43:17 +00:00
|
|
|
snprintf(infostr, sizeof(infostr)-1, "%s, %s UTC", ModeSpec[Mode].Name, rctime);
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_enter ();
|
|
|
|
gtk_label_set_text (GTK_LABEL(idlabel), "");
|
2011-08-20 11:49:29 +00:00
|
|
|
gtk_widget_set_sensitive (manualframe, false);
|
|
|
|
gtk_widget_set_sensitive (btnabort, true);
|
2011-08-16 21:29:38 +00:00
|
|
|
gtk_statusbar_push (GTK_STATUSBAR(statusbar), 0, "Receiving video..." );
|
2011-08-13 12:26:49 +00:00
|
|
|
gtk_label_set_markup (GTK_LABEL(infolabel), infostr);
|
|
|
|
gdk_threads_leave ();
|
2011-08-18 00:04:28 +00:00
|
|
|
printf(" getvideo @ %.1f Hz, Skip %d, HedrShift %d Hz\n", 44100.0, 0, HedrShift);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-20 11:49:29 +00:00
|
|
|
Finished = GetVideo(Mode, 44100, 0, false);
|
2011-08-13 12:26:49 +00:00
|
|
|
|
|
|
|
gdk_threads_enter ();
|
2011-08-20 11:49:29 +00:00
|
|
|
gtk_widget_set_sensitive (btnabort, false);
|
|
|
|
gtk_widget_set_sensitive (manualframe, true);
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_leave ();
|
2011-08-12 20:06:23 +00:00
|
|
|
|
2011-08-13 12:26:49 +00:00
|
|
|
id[0] = '\0';
|
2011-08-10 20:43:17 +00:00
|
|
|
|
2011-08-12 20:06:23 +00:00
|
|
|
if (Finished && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togfsk))) {
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_enter ();
|
2011-08-16 21:29:38 +00:00
|
|
|
gtk_statusbar_push (GTK_STATUSBAR(statusbar), 0, "Receiving FSK ID..." );
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_leave ();
|
2011-08-12 20:06:23 +00:00
|
|
|
GetFSK(id);
|
2011-08-13 12:26:49 +00:00
|
|
|
printf(" FSKID \"%s\"\n",id);
|
|
|
|
gdk_threads_enter ();
|
|
|
|
gtk_label_set_text (GTK_LABEL(idlabel), id);
|
|
|
|
gdk_threads_leave ();
|
2011-08-12 09:33:49 +00:00
|
|
|
}
|
2011-08-12 20:06:23 +00:00
|
|
|
|
2011-08-12 09:33:49 +00:00
|
|
|
snd_pcm_drop(pcm_handle);
|
|
|
|
|
|
|
|
if (Finished && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togslant))) {
|
2011-08-10 20:43:17 +00:00
|
|
|
|
|
|
|
// Fix slant
|
|
|
|
setVU(0,-100);
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_enter ();
|
2011-08-16 21:29:38 +00:00
|
|
|
gtk_statusbar_push (GTK_STATUSBAR(statusbar), 0, "Calculating slant..." );
|
2011-08-20 11:49:29 +00:00
|
|
|
gtk_widget_set_sensitive (vugrid, false);
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_leave ();
|
2011-08-18 00:04:28 +00:00
|
|
|
printf(" FindSync @ %.1f Hz\n",Rate);
|
|
|
|
Rate = FindSync(Mode, Rate, &Skip);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-10 20:43:17 +00:00
|
|
|
// Final image
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_enter ();
|
2011-08-16 21:29:38 +00:00
|
|
|
gtk_statusbar_push (GTK_STATUSBAR(statusbar), 0, "Redrawing..." );
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_leave ();
|
2011-08-18 00:04:28 +00:00
|
|
|
printf(" getvideo @ %.1f Hz, Skip %d, HedrShift %d Hz\n", Rate, Skip, HedrShift);
|
2011-08-20 11:49:29 +00:00
|
|
|
GetVideo(Mode, Rate, Skip, true);
|
2011-08-10 20:43:17 +00:00
|
|
|
}
|
2011-08-12 20:06:23 +00:00
|
|
|
|
|
|
|
free (HasSync);
|
2011-08-13 12:26:49 +00:00
|
|
|
HasSync = NULL;
|
|
|
|
|
2011-08-14 16:34:14 +00:00
|
|
|
// Add thumbnail to iconview
|
2012-12-25 20:43:26 +00:00
|
|
|
thumbbuf = gdk_pixbuf_scale_simple (RxPixbuf, 100,
|
|
|
|
100.0/ModeSpec[Mode].ImgWidth * ModeSpec[Mode].ImgHeight * ModeSpec[Mode].YScale, GDK_INTERP_HYPER);
|
2011-08-14 16:34:14 +00:00
|
|
|
gdk_threads_enter ();
|
2011-08-13 12:26:49 +00:00
|
|
|
gtk_list_store_prepend (savedstore, &iter);
|
|
|
|
gtk_list_store_set (savedstore, &iter, 0, thumbbuf, 1, id, -1);
|
2011-08-14 16:34:14 +00:00
|
|
|
gdk_threads_leave ();
|
2011-08-10 20:43:17 +00:00
|
|
|
|
|
|
|
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(togsave))) {
|
|
|
|
|
|
|
|
// Save the raw signal
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_enter ();
|
2011-08-16 21:29:38 +00:00
|
|
|
gtk_statusbar_push (GTK_STATUSBAR(statusbar), 0, "Saving..." );
|
2011-08-13 12:26:49 +00:00
|
|
|
gdk_threads_leave ();
|
2011-08-10 20:43:17 +00:00
|
|
|
|
2011-08-13 12:26:49 +00:00
|
|
|
setVU(0,-100);
|
2011-08-10 20:43:17 +00:00
|
|
|
|
2012-12-24 00:31:40 +00:00
|
|
|
ensure_dir_exists("rx-lum");
|
2011-08-10 20:43:17 +00:00
|
|
|
LumFile = fopen(lumfilename,"w");
|
|
|
|
if (LumFile == NULL) {
|
|
|
|
perror("Unable to open luma file for writing");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
2011-08-18 00:04:28 +00:00
|
|
|
fwrite(StoredLum,1,(ModeSpec[Mode].LineLen * ModeSpec[Mode].ImgHeight) * 44100,LumFile);
|
2011-08-10 20:43:17 +00:00
|
|
|
fclose(LumFile);
|
|
|
|
|
|
|
|
// Save the received image as PNG
|
|
|
|
png_t png;
|
|
|
|
png_init(0,0);
|
|
|
|
|
2011-08-14 16:34:14 +00:00
|
|
|
GdkPixbuf *scaledpb;
|
2012-12-25 20:43:26 +00:00
|
|
|
scaledpb = gdk_pixbuf_scale_simple (RxPixbuf, ModeSpec[Mode].ImgWidth,
|
|
|
|
ModeSpec[Mode].ImgHeight * ModeSpec[Mode].YScale, GDK_INTERP_HYPER);
|
2011-08-14 16:34:14 +00:00
|
|
|
pixels = gdk_pixbuf_get_pixels(scaledpb);
|
2011-08-10 20:43:17 +00:00
|
|
|
|
2012-12-24 00:31:40 +00:00
|
|
|
ensure_dir_exists("rx");
|
2011-08-10 20:43:17 +00:00
|
|
|
png_open_file_write(&png, pngfilename);
|
2011-08-14 16:34:14 +00:00
|
|
|
png_set_data(&png, ModeSpec[Mode].ImgWidth, ModeSpec[Mode].ImgHeight * ModeSpec[Mode].YScale, 8, PNG_TRUECOLOR, pixels);
|
2011-08-10 20:43:17 +00:00
|
|
|
png_close_file(&png);
|
2011-08-14 16:34:14 +00:00
|
|
|
g_object_unref(scaledpb);
|
2011-07-07 14:09:57 +00:00
|
|
|
}
|
|
|
|
|
2011-08-18 00:04:28 +00:00
|
|
|
free(StoredLum);
|
|
|
|
StoredLum = NULL;
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-29 20:09:42 +00:00
|
|
|
|
2011-07-07 14:09:57 +00:00
|
|
|
/*
|
|
|
|
* main
|
|
|
|
*/
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
|
|
|
|
pthread_t thread1;
|
|
|
|
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
|
2012-12-25 20:43:26 +00:00
|
|
|
//g_thread_init (NULL);
|
2011-07-07 14:09:57 +00:00
|
|
|
gdk_threads_init ();
|
|
|
|
|
|
|
|
createGUI();
|
2011-08-07 17:46:35 +00:00
|
|
|
initPcmDevice();
|
2011-07-07 14:09:57 +00:00
|
|
|
|
2011-08-07 17:46:35 +00:00
|
|
|
pthread_create (&thread1, NULL, Listen, NULL);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
gtk_main();
|
|
|
|
|
2011-08-11 11:48:35 +00:00
|
|
|
g_object_unref(RxPixbuf);
|
2011-08-18 00:04:28 +00:00
|
|
|
free(StoredLum);
|
2011-07-07 14:09:57 +00:00
|
|
|
|
|
|
|
printf("Clean exit\n");
|
|
|
|
|
|
|
|
return (EXIT_SUCCESS);
|
|
|
|
}
|