diff --git a/.gitignore b/.gitignore index 09ac174..8a64134 100644 --- a/.gitignore +++ b/.gitignore @@ -1,20 +1,3 @@ slowrx rx rx-lum -Makefile.in -/autom4te.cache -/aclocal.m4 -/compile -/configure -/depcomp -/install-sh -/missing -/stamp-h1 -Makefile -autoscan.log -config.h -config.h.in -config.log -config.status -configure.scan -.deps diff --git a/configure.ac b/configure.ac index b7ec295..bf8f559 100644 --- a/configure.ac +++ b/configure.ac @@ -1,11 +1,10 @@ AC_INIT([slowrx], [0.1], [oona@kapsi.fi]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) -AX_CXX_COMPILE_STDCXX_11() AC_PROG_CXX AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ Makefile - src/uus/Makefile + src/Makefile ]) PKG_CHECK_MODULES([GTKMM], [gtkmm-3.0 >= 3.8.0]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index b2da63b..3aeacc4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ bin_PROGRAMS = slowrx slowrx_CPPFLAGS = $(GTKMM_CFLAGS) slowrx_LDADD = $(GTKMM_LIBS) -slowrx_SOURCES = slowrx.cc modespec.cc +slowrx_SOURCES = slowrx.cc common.cc gui.cc fsk.cc modespec.cc pcm.cc sync.cc video.cc vis.cc diff --git a/src/common.h b/src/common.h index a02b307..471c170 100644 --- a/src/common.h +++ b/src/common.h @@ -6,6 +6,15 @@ #define BUFLEN 4096 #define SYNCPIXLEN 1.5e-3 +extern gboolean Abort; +extern gboolean Adaptive; +extern gboolean *HasSync; +extern gboolean ManualActivated; +extern gboolean ManualResync; +extern guchar *StoredLum; +extern pthread_t thread1; +extern guchar VISmap[]; + typedef struct _FFTStuff FFTStuff; struct _FFTStuff { double *in; @@ -17,51 +26,49 @@ extern FFTStuff fft; typedef struct _PcmData PcmData; struct _PcmData { -// snd_pcm_t *handle; - void *handle; + snd_pcm_t *handle; gint16 *Buffer; int WindowPtr; gboolean BufferDrop; }; -//extern PcmData pcm; +extern PcmData pcm; typedef struct _GuiObjs GuiObjs; struct _GuiObjs { - Gtk::Button *button_abort; - Gtk::Button *button_browse; - Gtk::Button *button_clear; - Gtk::Button *button_start; - Gtk::ComboBox *combo_card; - Gtk::ComboBox *combo_mode; - Gtk::Widget *entry_picdir; - Gtk::Widget *eventbox_img; - Gtk::Widget *frame_manual; - Gtk::Widget *frame_slant; - Gtk::Widget *grid_vu; - Gtk::Widget *iconview; - Gtk::Widget *image_devstatus; - Gtk::Widget *image_pwr; - Gtk::Widget *image_rx; - Gtk::Widget *image_snr; - Gtk::Label *label_fskid; - Gtk::Label *label_lastmode; - Gtk::Label *label_utc; - Gtk::MenuItem *menuitem_about; - Gtk::MenuItem *menuitem_quit; - Gtk::SpinButton *spin_shift; - Gtk::Widget *statusbar; - Gtk::ToggleButton *tog_adapt; - Gtk::ToggleButton *tog_fsk; - Gtk::ToggleButton *tog_rx; - Gtk::ToggleButton *tog_save; - Gtk::ToggleButton *tog_setedge; - Gtk::ToggleButton *tog_slant; - Gtk::Window *window_about; - Gtk::Window *window_main; + GtkWidget *button_abort; + GtkWidget *button_browse; + GtkWidget *button_clear; + GtkWidget *button_start; + GtkWidget *combo_card; + GtkWidget *combo_mode; + GtkWidget *entry_picdir; + GtkWidget *eventbox_img; + GtkWidget *frame_manual; + GtkWidget *frame_slant; + GtkWidget *grid_vu; + GtkWidget *iconview; + GtkWidget *image_devstatus; + GtkWidget *image_pwr; + GtkWidget *image_rx; + GtkWidget *image_snr; + GtkWidget *label_fskid; + GtkWidget *label_lastmode; + GtkWidget *label_utc; + GtkWidget *menuitem_about; + GtkWidget *menuitem_quit; + GtkWidget *spin_shift; + GtkWidget *statusbar; + GtkWidget *tog_adapt; + GtkWidget *tog_fsk; + GtkWidget *tog_rx; + GtkWidget *tog_save; + GtkWidget *tog_setedge; + GtkWidget *tog_slant; + GtkWidget *window_about; + GtkWidget *window_main; }; extern GuiObjs gui; -/* extern GdkPixbuf *pixbuf_PWR; extern GdkPixbuf *pixbuf_SNR; extern GdkPixbuf *pixbuf_rx; @@ -69,13 +76,13 @@ extern GdkPixbuf *pixbuf_disp; extern GtkListStore *savedstore; -extern GKeyFile *config;*/ +extern GKeyFile *config; typedef struct _PicMeta PicMeta; struct _PicMeta { - int HedrShift; - int Mode; + gshort HedrShift; + guchar Mode; double Rate; int Skip; GdkPixbuf *thumbbuf; @@ -100,36 +107,36 @@ enum { }; typedef struct ModeSpec { - std::string Name; - std::string ShortName; + char *Name; + char *ShortName; double SyncTime; double PorchTime; double SeptrTime; double PixelTime; double LineTime; - int ImgWidth; - int NumLines; - int LineHeight; - int ColorEnc; + gushort ImgWidth; + gushort NumLines; + guchar LineHeight; + guchar ColorEnc; } _ModeSpec; extern _ModeSpec ModeSpec[]; -double power (fftw_complex coeff); -int clip (double a); +double power (fftw_complex coeff); +guchar clip (double a); void createGUI (); double deg2rad (double Deg); -double FindSync (int Mode, double Rate, int *Skip); +double FindSync (guchar Mode, double Rate, int *Skip); void GetFSK (char *dest); -bool GetVideo (int Mode, double Rate, int Skip, bool Redraw); -int GetVIS (); -int GetBin (double Freq, int FFTLen); -int initPcmDevice (char *); +gboolean GetVideo (guchar Mode, double Rate, int Skip, gboolean Redraw); +guchar GetVIS (); +guint GetBin (double Freq, guint FFTLen); +int initPcmDevice (); void *Listen (); void populateDeviceList (); -void readPcm (int numsamples); +void readPcm (gint numsamples); void saveCurrentPic(); -void setVU (double *Power, int FFTLen, int WinIdx, bool ShowWin); +void setVU (double *Power, int FFTLen, int WinIdx, gboolean ShowWin); void evt_AbortRx (); void evt_changeDevices (); diff --git a/src/modespec.cc b/src/modespec.cc index 15c6ce1..3231612 100644 --- a/src/modespec.cc +++ b/src/modespec.cc @@ -1,4 +1,7 @@ -#include +#include +#include +#include +#include #include diff --git a/src/slowrx.cc b/src/slowrx.cc index da8d285..d784fd2 100644 --- a/src/slowrx.cc +++ b/src/slowrx.cc @@ -1,21 +1,256 @@ -#include +/* + * slowrx - an SSTV decoder + * * * * * * * * * * * * * * + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + #include + #include "common.h" -int startGui (int, char**); +// The thread that listens to VIS headers and calls decoders etc +void *Listen() { + + char rctime[8]; + + guchar Mode=0; + struct tm *timeptr = NULL; + time_t timet; + gboolean Finished; + char id[20]; + GtkTreeIter iter; + + while (TRUE) { + + gdk_threads_enter (); + gtk_widget_set_sensitive (gui.grid_vu, TRUE); + gtk_widget_set_sensitive (gui.button_abort, FALSE); + gtk_widget_set_sensitive (gui.button_clear, TRUE); + gdk_threads_leave (); + + pcm.WindowPtr = 0; + snd_pcm_prepare(pcm.handle); + snd_pcm_start (pcm.handle); + Abort = FALSE; + + do { + + // Wait for VIS + Mode = GetVIS(); + + // Stop listening on ALSA error + if (Abort) pthread_exit(NULL); + + // If manual resync was requested, redraw image + if (ManualResync) { + ManualResync = FALSE; + snd_pcm_drop(pcm.handle); + printf("getvideo at %.2f skip %d\n",CurrentPic.Rate,CurrentPic.Skip); + GetVideo(CurrentPic.Mode, CurrentPic.Rate, CurrentPic.Skip, TRUE); + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(gui.tog_save))) + saveCurrentPic(); + pcm.WindowPtr = 0; + snd_pcm_prepare(pcm.handle); + snd_pcm_start (pcm.handle); + } + + } while (Mode == 0); + + // Start reception + + CurrentPic.Rate = 44100; + CurrentPic.Mode = Mode; + + printf(" ==== %s ====\n", ModeSpec[CurrentPic.Mode].Name); + + // Store time of reception + timet = time(NULL); + timeptr = gmtime(&timet); + strftime(CurrentPic.timestr, sizeof(CurrentPic.timestr)-1,"%Y%m%d-%H%M%Sz", timeptr); + + + // Allocate space for cached Lum + free(StoredLum); + StoredLum = calloc( (int)((ModeSpec[CurrentPic.Mode].LineTime * ModeSpec[CurrentPic.Mode].NumLines + 1) * 44100), sizeof(guchar)); + if (StoredLum == NULL) { + perror("Listen: Unable to allocate memory for Lum"); + exit(EXIT_FAILURE); + } + + // Allocate space for sync signal + HasSync = calloc((int)(ModeSpec[CurrentPic.Mode].LineTime * ModeSpec[CurrentPic.Mode].NumLines / (13.0/44100) +1), sizeof(gboolean)); + if (HasSync == NULL) { + perror("Listen: Unable to allocate memory for sync signal"); + exit(EXIT_FAILURE); + } + + // Get video + strftime(rctime, sizeof(rctime)-1, "%H:%Mz", timeptr); + gdk_threads_enter (); + gtk_label_set_text (GTK_LABEL(gui.label_fskid), ""); + gtk_widget_set_sensitive (gui.frame_manual, FALSE); + gtk_widget_set_sensitive (gui.frame_slant, FALSE); + gtk_widget_set_sensitive (gui.combo_card, FALSE); + gtk_widget_set_sensitive (gui.button_abort, TRUE); + gtk_widget_set_sensitive (gui.button_clear, FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gui.tog_setedge), FALSE); + gtk_statusbar_push (GTK_STATUSBAR(gui.statusbar), 0, "Receiving video..." ); + gtk_label_set_markup (GTK_LABEL(gui.label_lastmode), ModeSpec[CurrentPic.Mode].Name); + gtk_label_set_markup (GTK_LABEL(gui.label_utc), rctime); + gdk_threads_leave (); + printf(" getvideo @ %.1f Hz, Skip %d, HedrShift %+d Hz\n", 44100.0, 0, CurrentPic.HedrShift); + + Finished = GetVideo(CurrentPic.Mode, 44100, 0, FALSE); + + gdk_threads_enter (); + gtk_widget_set_sensitive (gui.button_abort, FALSE); + gdk_threads_leave (); + + id[0] = '\0'; + + if (Finished && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gui.tog_fsk))) { + gdk_threads_enter (); + gtk_statusbar_push (GTK_STATUSBAR(gui.statusbar), 0, "Receiving FSK ID..." ); + gdk_threads_leave (); + GetFSK(id); + printf(" FSKID \"%s\"\n",id); + gdk_threads_enter (); + gtk_label_set_text (GTK_LABEL(gui.label_fskid), id); + gdk_threads_leave (); + } + + snd_pcm_drop(pcm.handle); + + if (Finished && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gui.tog_slant))) { + + // Fix slant + //setVU(0,6); + gdk_threads_enter (); + gtk_statusbar_push (GTK_STATUSBAR(gui.statusbar), 0, "Calculating slant..." ); + gtk_widget_set_sensitive (gui.grid_vu, FALSE); + gdk_threads_leave (); + printf(" FindSync @ %.1f Hz\n",CurrentPic.Rate); + CurrentPic.Rate = FindSync(CurrentPic.Mode, CurrentPic.Rate, &CurrentPic.Skip); + + // Final image + printf(" getvideo @ %.1f Hz, Skip %d, HedrShift %+d Hz\n", CurrentPic.Rate, CurrentPic.Skip, CurrentPic.HedrShift); + GetVideo(CurrentPic.Mode, CurrentPic.Rate, CurrentPic.Skip, TRUE); + } + + free (HasSync); + HasSync = NULL; + + // Add thumbnail to iconview + CurrentPic.thumbbuf = gdk_pixbuf_scale_simple (pixbuf_rx, 100, + 100.0/ModeSpec[CurrentPic.Mode].ImgWidth * ModeSpec[CurrentPic.Mode].NumLines * ModeSpec[CurrentPic.Mode].LineHeight, GDK_INTERP_HYPER); + gdk_threads_enter (); + gtk_list_store_prepend (savedstore, &iter); + gtk_list_store_set (savedstore, &iter, 0, CurrentPic.thumbbuf, 1, id, -1); + gdk_threads_leave (); + + // Save PNG + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(gui.tog_save))) { + + //setVU(0,6); + + /*ensure_dir_exists("rx-lum"); + LumFile = fopen(lumfilename,"w"); + if (LumFile == NULL) + perror("Unable to open luma file for writing"); + fwrite(StoredLum,1,(ModeSpec[Mode].LineTime * ModeSpec[Mode].NumLines) * 44100,LumFile); + fclose(LumFile);*/ + + saveCurrentPic(); + } + + gdk_threads_enter (); + gtk_widget_set_sensitive (gui.frame_slant, TRUE); + gtk_widget_set_sensitive (gui.frame_manual, TRUE); + gtk_widget_set_sensitive (gui.combo_card, TRUE); + gdk_threads_leave (); + + } +} + + +/* + * main + */ int main(int argc, char *argv[]) { - return startGui(argc, argv); -} - -int startGui(int argc, char *argv[]) { - Glib::RefPtr app = - Gtk::Application::create(argc, argv, - "com.windytan.slowrx"); - - Glib::RefPtr builder = Gtk::Builder::create_from_file("ui/slowrx.ui"); - Gtk::Window *pWindow; - builder->get_widget("window_main", pWindow); - - return app->run(*pWindow); + + FILE *ConfFile; + const gchar *confdir; + GString *confpath; + gchar *confdata; + gsize *keylen=NULL; + + gtk_init (&argc, &argv); + + gdk_threads_init (); + + // Load config + confdir = g_get_user_config_dir(); + confpath = g_string_new(confdir); + g_string_append(confpath, "/slowrx.ini"); + + config = g_key_file_new(); + if (g_key_file_load_from_file(config, confpath->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) { + + } else { + printf("No valid config file found\n"); + g_key_file_load_from_data(config, "[slowrx]\ndevice=default", -1, G_KEY_FILE_NONE, NULL); + } + + // Prepare FFT + fft.in = fftw_alloc_real(2048); + if (fft.in == NULL) { + perror("GetVideo: Unable to allocate memory for FFT"); + exit(EXIT_FAILURE); + } + fft.out = fftw_alloc_complex(2048); + if (fft.out == NULL) { + perror("GetVideo: Unable to allocate memory for FFT"); + fftw_free(fft.in); + exit(EXIT_FAILURE); + } + memset(fft.in, 0, sizeof(double) * 2048); + + fft.Plan1024 = fftw_plan_dft_r2c_1d(1024, fft.in, fft.out, FFTW_ESTIMATE); + fft.Plan2048 = fftw_plan_dft_r2c_1d(2048, fft.in, fft.out, FFTW_ESTIMATE); + + createGUI(); + populateDeviceList(); + + gtk_main(); + + // Save config on exit + ConfFile = fopen(confpath->str,"w"); + if (ConfFile == NULL) { + perror("Unable to open config file for writing"); + } else { + confdata = g_key_file_to_data(config,keylen,NULL); + fprintf(ConfFile,"%s",confdata); + fwrite(confdata,1,(size_t)keylen,ConfFile); + fclose(ConfFile); + } + + g_object_unref(pixbuf_rx); + free(StoredLum); + fftw_free(fft.in); + fftw_free(fft.out); + + return (EXIT_SUCCESS); }