#include #include #include #include #include #include #include #include #include "common.h" gboolean Abort = FALSE; gboolean Adaptive = TRUE; gboolean *HasSync = NULL; gshort HedrShift = 0; gboolean ManualActivated = FALSE; gboolean ManualResync = FALSE; guchar *StoredLum = NULL; pthread_t thread1; FFTStuff fft; GuiObjs gui; PicMeta CurrentPic; PcmData pcm; GdkPixbuf *pixbuf_rx = NULL; GdkPixbuf *pixbuf_disp = NULL; GdkPixbuf *pixbuf_PWR = NULL; GdkPixbuf *pixbuf_SNR = NULL; GtkListStore *savedstore = NULL; GKeyFile *config = NULL; // Return the FFT bin index matching the given frequency guint GetBin (double Freq, guint FFTLen) { return (Freq / 44100 * FFTLen); } // Sinusoid power from complex DFT coefficients double power (fftw_complex coeff) { return pow(coeff[0],2) + pow(coeff[1],2); } // Clip to [0..255] guchar clip (double a) { if (a < 0) return 0; else if (a > 255) return 255; return (guchar)round(a); } // Convert degrees -> radians double deg2rad (double Deg) { return (Deg / 180) * M_PI; } // Convert radians -> degrees double rad2deg (double rad) { return (180 / M_PI) * rad; } 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); } } } // Save current picture as PNG void saveCurrentPic() { GdkPixbuf *scaledpb; GString *pngfilename; pngfilename = g_string_new(g_key_file_get_string(config,"slowrx","rxdir",NULL)); g_string_append_printf(pngfilename, "/%s_%s.png", CurrentPic.timestr, ModeSpec[CurrentPic.Mode].ShortName); printf(" Saving to %s\n", pngfilename->str); scaledpb = gdk_pixbuf_scale_simple (pixbuf_rx, ModeSpec[CurrentPic.Mode].ImgWidth, ModeSpec[CurrentPic.Mode].NumLines * ModeSpec[CurrentPic.Mode].LineHeight, GDK_INTERP_HYPER); ensure_dir_exists(g_key_file_get_string(config,"slowrx","rxdir",NULL)); gdk_pixbuf_savev(scaledpb, pngfilename->str, "png", NULL, NULL, NULL); g_object_unref(scaledpb); g_string_free(pngfilename, TRUE); } /*** Gtk+ event handlers ***/ // Quit void evt_deletewindow() { gtk_main_quit (); } // Transform the NoiseAdapt toggle state into a variable void evt_GetAdaptive() { Adaptive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(gui.tog_adapt)); } // Manual Start clicked void evt_ManualStart() { ManualActivated = TRUE; } // Abort clicked during rx void evt_AbortRx() { Abort = TRUE; } // Another device selected from list void evt_changeDevices() { int status; pcm.BufferDrop = FALSE; Abort = TRUE; static int init; if (init) pthread_join(thread1, NULL); init = 1; if (pcm.handle != NULL) snd_pcm_close(pcm.handle); status = initPcmDevice(gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(gui.combo_card))); switch(status) { case 0: gtk_image_set_from_stock(GTK_IMAGE(gui.image_devstatus),GTK_STOCK_YES,GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_widget_set_tooltip_text(gui.image_devstatus, "Device successfully opened"); break; case -1: gtk_image_set_from_stock(GTK_IMAGE(gui.image_devstatus),GTK_STOCK_DIALOG_WARNING,GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_widget_set_tooltip_text(gui.image_devstatus, "Device was opened, but doesn't support 44100 Hz"); break; case -2: gtk_image_set_from_stock(GTK_IMAGE(gui.image_devstatus),GTK_STOCK_DIALOG_ERROR,GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_widget_set_tooltip_text(gui.image_devstatus, "Failed to open device"); break; } g_key_file_set_string(config,"slowrx","device",gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(gui.combo_card))); pthread_create (&thread1, NULL, Listen, NULL); } // Clear received picture & metadata void evt_clearPix() { gdk_pixbuf_fill (pixbuf_disp, 0); gtk_image_set_from_pixbuf(GTK_IMAGE(gui.image_rx), pixbuf_disp); gtk_label_set_markup (GTK_LABEL(gui.label_fskid), ""); gtk_label_set_markup (GTK_LABEL(gui.label_utc), ""); gtk_label_set_markup (GTK_LABEL(gui.label_lastmode), ""); } // Manual slant adjust void evt_clickimg(GtkWidget *widget, GdkEventButton* event, GdkWindowEdge edge) { static double prevx=0,prevy=0,newrate; static gboolean secondpress=FALSE; double x,y,dx,dy,xic; (void)widget; (void)edge; if (event->type == GDK_BUTTON_PRESS && event->button == 1 && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(gui.tog_setedge))) { x = event->x * (ModeSpec[CurrentPic.Mode].ImgWidth / 500.0); y = event->y * (ModeSpec[CurrentPic.Mode].ImgWidth / 500.0) / ModeSpec[CurrentPic.Mode].LineHeight; if (secondpress) { secondpress=FALSE; dx = x - prevx; dy = y - prevy; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gui.tog_setedge),FALSE); // Adjust sample rate, if in sensible limits newrate = CurrentPic.Rate + CurrentPic.Rate * (dx * ModeSpec[CurrentPic.Mode].PixelTime) / (dy * ModeSpec[CurrentPic.Mode].LineHeight * ModeSpec[CurrentPic.Mode].LineTime); if (newrate > 32000 && newrate < 56000) { CurrentPic.Rate = newrate; // Find x-intercept and adjust skip xic = fmod( (x - (y / (dy/dx))), ModeSpec[CurrentPic.Mode].ImgWidth); if (xic < 0) xic = ModeSpec[CurrentPic.Mode].ImgWidth - xic; CurrentPic.Skip = fmod(CurrentPic.Skip + xic * ModeSpec[CurrentPic.Mode].PixelTime * CurrentPic.Rate, ModeSpec[CurrentPic.Mode].LineTime * CurrentPic.Rate); if (CurrentPic.Skip > ModeSpec[CurrentPic.Mode].LineTime * CurrentPic.Rate / 2.0) CurrentPic.Skip -= ModeSpec[CurrentPic.Mode].LineTime * CurrentPic.Rate; // Signal the listener to exit from GetVIS() and re-process the pic ManualResync = TRUE; } } else { secondpress = TRUE; prevx = x; prevy = y; } } else { secondpress=FALSE; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gui.tog_setedge), FALSE); } }