kopia lustrzana https://github.com/windytan/slowrx
working manual slant adjustment; CurrentPic struct; etc
rodzic
2fccddba62
commit
0ad0414c41
111
common.c
111
common.c
|
@ -2,6 +2,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
|
@ -10,31 +12,33 @@
|
|||
|
||||
#include "common.h"
|
||||
|
||||
bool Abort = false;
|
||||
bool Adaptive = true;
|
||||
bool BufferDrop = false;
|
||||
double *in = NULL;
|
||||
bool *HasSync = NULL;
|
||||
gshort HedrShift = 0;
|
||||
bool ManualActivated = false;
|
||||
bool ManualResync = false;
|
||||
int MaxPcm = 0;
|
||||
double *out = NULL;
|
||||
gint16 *PcmBuffer = NULL;
|
||||
int PcmPointer = 0;
|
||||
int MaxPcm = 0;
|
||||
guchar *StoredLum = NULL;
|
||||
bool *HasSync = NULL;
|
||||
double *in = NULL;
|
||||
double *out = NULL;
|
||||
gshort HedrShift = 0;
|
||||
bool Adaptive = true;
|
||||
bool ManualActivated = false;
|
||||
bool Abort = false;
|
||||
bool BufferDrop = false;
|
||||
|
||||
pthread_t thread1;
|
||||
|
||||
GuiObjs gui;
|
||||
PicMeta CurrentPic;
|
||||
|
||||
GdkPixbuf *pixbuf_rx = NULL;
|
||||
GdkPixbuf *pixbuf_disp = NULL;
|
||||
GdkPixbuf *pixbuf_PWR = NULL;
|
||||
GdkPixbuf *pixbuf_SNR = NULL;
|
||||
GdkPixbuf *pixbuf_rx = NULL;
|
||||
GdkPixbuf *pixbuf_disp = NULL;
|
||||
GdkPixbuf *pixbuf_PWR = NULL;
|
||||
GdkPixbuf *pixbuf_SNR = NULL;
|
||||
|
||||
GtkListStore *savedstore = NULL;
|
||||
|
||||
GKeyFile *keyfile = NULL;
|
||||
GKeyFile *config = NULL;
|
||||
|
||||
snd_pcm_t *pcm_handle = NULL;
|
||||
|
||||
|
@ -63,6 +67,36 @@ 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].ImgHeight * ModeSpec[CurrentPic.Mode].YScale, 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 ***/
|
||||
|
||||
|
@ -117,7 +151,7 @@ void evt_changeDevices() {
|
|||
break;
|
||||
}
|
||||
|
||||
g_key_file_set_string(keyfile,"slowrx","device",gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(gui.combo_card)));
|
||||
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);
|
||||
|
||||
|
@ -134,25 +168,50 @@ void evt_clearPix() {
|
|||
|
||||
// Manual slant adjust
|
||||
void evt_clickimg(GtkWidget *widget, GdkEventButton* event, GdkWindowEdge edge) {
|
||||
static double prevx=0,prevy=0;
|
||||
static double prevx=0,prevy=0,newrate;
|
||||
static bool secondpress=false;
|
||||
double dx,dy,a;
|
||||
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))) {
|
||||
if (secondpress) {
|
||||
printf(":) %.1f,%.1f -> %.1f,%.1f\n",prevx,prevy,event->x,event->y);
|
||||
dx = event->x - prevx;
|
||||
dy = event->y - prevy;
|
||||
a = rad2deg(M_PI/2 - asin(dx/sqrt(pow(dx,2) + pow(dy,2))));
|
||||
if (event->y < prevy) a = fabs(a-180);
|
||||
printf("%.3f\n",a);
|
||||
|
||||
x = event->x * (ModeSpec[CurrentPic.Mode].ImgWidth / 500.0);
|
||||
y = event->y * (ModeSpec[CurrentPic.Mode].ImgWidth / 500.0) / ModeSpec[CurrentPic.Mode].YScale;
|
||||
|
||||
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].PixelLen) / (dy * ModeSpec[CurrentPic.Mode].YScale * ModeSpec[CurrentPic.Mode].LineLen);
|
||||
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].PixelLen * CurrentPic.Rate,
|
||||
ModeSpec[CurrentPic.Mode].LineLen * CurrentPic.Rate);
|
||||
if (CurrentPic.Skip > ModeSpec[CurrentPic.Mode].LineLen * CurrentPic.Rate / 2.0)
|
||||
CurrentPic.Skip -= ModeSpec[CurrentPic.Mode].LineLen * CurrentPic.Rate;
|
||||
|
||||
// Signal the listener to exit from GetVIS() and re-process the pic
|
||||
ManualResync = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
prevx = event->x;
|
||||
prevy = event->y;
|
||||
secondpress = true;
|
||||
prevx = x;
|
||||
prevy = y;
|
||||
}
|
||||
} else {
|
||||
secondpress=false;
|
||||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(gui.tog_setedge), false);
|
||||
}
|
||||
}
|
||||
|
|
23
common.h
23
common.h
|
@ -10,9 +10,9 @@ extern bool Abort;
|
|||
extern bool Adaptive;
|
||||
extern bool BufferDrop;
|
||||
extern bool *HasSync;
|
||||
extern gshort HedrShift;
|
||||
extern double *in;
|
||||
extern bool ManualActivated;
|
||||
extern bool ManualResync;
|
||||
extern int MaxPcm;
|
||||
extern double *out;
|
||||
extern gint16 *PcmBuffer;
|
||||
|
@ -64,13 +64,25 @@ extern GdkPixbuf *pixbuf_disp;
|
|||
|
||||
extern GtkListStore *savedstore;
|
||||
|
||||
extern GKeyFile *keyfile;
|
||||
extern GKeyFile *config;
|
||||
|
||||
extern snd_pcm_t *pcm_handle;
|
||||
|
||||
extern fftw_plan Plan1024;
|
||||
extern fftw_plan Plan2048;
|
||||
|
||||
typedef struct _PicMeta PicMeta;
|
||||
struct _PicMeta {
|
||||
gshort HedrShift;
|
||||
guchar Mode;
|
||||
double Rate;
|
||||
int Skip;
|
||||
GdkPixbuf *thumbbuf;
|
||||
char timestr[40];
|
||||
};
|
||||
|
||||
extern PicMeta CurrentPic;
|
||||
|
||||
// SSTV modes
|
||||
enum {
|
||||
UNKNOWN=0,
|
||||
|
@ -87,7 +99,7 @@ enum {
|
|||
GBR, RGB, YUV, BW
|
||||
};
|
||||
|
||||
typedef struct ModeSpecDef {
|
||||
typedef struct ModeSpec {
|
||||
char *Name;
|
||||
char *ShortName;
|
||||
double SyncLen;
|
||||
|
@ -99,9 +111,9 @@ typedef struct ModeSpecDef {
|
|||
gushort ImgHeight;
|
||||
guchar YScale;
|
||||
guchar ColorEnc;
|
||||
} ModeSpecDef;
|
||||
} _ModeSpec;
|
||||
|
||||
extern ModeSpecDef ModeSpec[];
|
||||
extern _ModeSpec ModeSpec[];
|
||||
|
||||
guchar clip (double a);
|
||||
void createGUI ();
|
||||
|
@ -115,6 +127,7 @@ int initPcmDevice ();
|
|||
void *Listen ();
|
||||
void populateDeviceList ();
|
||||
void readPcm (gint numsamples);
|
||||
void saveCurrentPic();
|
||||
void setVU (short int PcmValue, double SNRdB);
|
||||
|
||||
void evt_AbortRx ();
|
||||
|
|
6
fsk.c
6
fsk.c
|
@ -54,9 +54,9 @@ void GetFSK (char *dest) {
|
|||
// FFT of last 22 ms
|
||||
fftw_execute(Plan2048);
|
||||
|
||||
LoBin = GetBin(1900+HedrShift, FFTLen)-1;
|
||||
MidBin = GetBin(2000+HedrShift, FFTLen);
|
||||
HiBin = GetBin(2100+HedrShift, FFTLen)+1;
|
||||
LoBin = GetBin(1900+CurrentPic.HedrShift, FFTLen)-1;
|
||||
MidBin = GetBin(2000+CurrentPic.HedrShift, FFTLen);
|
||||
HiBin = GetBin(2100+CurrentPic.HedrShift, FFTLen)+1;
|
||||
|
||||
LoPow = 0;
|
||||
HiPow = 0;
|
||||
|
|
10
gui.c
10
gui.c
|
@ -71,11 +71,11 @@ void createGUI() {
|
|||
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(gui.combo_mode), 0);
|
||||
|
||||
if (g_key_file_get_string(keyfile,"slowrx","rxdir",NULL) != NULL) {
|
||||
gtk_entry_set_text(GTK_ENTRY(gui.entry_picdir),g_key_file_get_string(keyfile,"slowrx","rxdir",NULL));
|
||||
if (g_key_file_get_string(config,"slowrx","rxdir",NULL) != NULL) {
|
||||
gtk_entry_set_text(GTK_ENTRY(gui.entry_picdir),g_key_file_get_string(config,"slowrx","rxdir",NULL));
|
||||
} else {
|
||||
g_key_file_set_string(keyfile,"slowrx","rxdir",g_get_home_dir());
|
||||
gtk_entry_set_text(GTK_ENTRY(gui.entry_picdir),g_key_file_get_string(keyfile,"slowrx","rxdir",NULL));
|
||||
g_key_file_set_string(config,"slowrx","rxdir",g_get_home_dir());
|
||||
gtk_entry_set_text(GTK_ENTRY(gui.entry_picdir),g_key_file_get_string(config,"slowrx","rxdir",NULL));
|
||||
}
|
||||
|
||||
setVU(0, -100);
|
||||
|
@ -146,7 +146,7 @@ void evt_chooseDir() {
|
|||
NULL);
|
||||
|
||||
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
|
||||
g_key_file_set_string(keyfile,"slowrx","rxdir",gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
|
||||
g_key_file_set_string(config,"slowrx","rxdir",gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
|
||||
gtk_entry_set_text(GTK_ENTRY(gui.entry_picdir),gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)));
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
* <http://www.tima.com/~djones/line.txt>
|
||||
*/
|
||||
|
||||
ModeSpecDef ModeSpec[] = {
|
||||
_ModeSpec ModeSpec[] = {
|
||||
|
||||
[M1] = { // N7CXI, 2000
|
||||
.Name = "Martin M1",
|
||||
|
|
2
pcm.c
2
pcm.c
|
@ -83,7 +83,7 @@ void populateDeviceList() {
|
|||
snd_card_get_name(card,&cardname);
|
||||
gdk_threads_enter();
|
||||
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(gui.combo_card), cardname);
|
||||
if (strcmp(cardname,g_key_file_get_string(keyfile,"slowrx","device",NULL)) == 0)
|
||||
if (strcmp(cardname,g_key_file_get_string(config,"slowrx","device",NULL)) == 0)
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(gui.combo_card), row);
|
||||
|
||||
gdk_threads_leave();
|
||||
|
|
153
slowrx.c
153
slowrx.c
|
@ -21,52 +21,18 @@
|
|||
|
||||
#include "common.h"
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The thread that listens to VIS headers and calls decoders etc
|
||||
void *Listen() {
|
||||
|
||||
int Skip = 0;
|
||||
char timestr[40], rctime[8];
|
||||
GString *pngfilename;
|
||||
char rctime[8];
|
||||
|
||||
guchar Mode=0;
|
||||
double Rate;
|
||||
struct tm *timeptr = NULL;
|
||||
time_t timet;
|
||||
bool Finished;
|
||||
GdkPixbuf *thumbbuf;
|
||||
char id[20];
|
||||
GtkTreeIter iter;
|
||||
|
||||
/** 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);
|
||||
}
|
||||
memset(in, 0, sizeof(double) * 2048);
|
||||
memset(out, 0, sizeof(double) * 2048);
|
||||
|
||||
Plan1024 = fftw_plan_r2r_1d(1024, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||
Plan2048 = fftw_plan_r2r_1d(2048, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||
|
||||
|
||||
while (true) {
|
||||
|
||||
gdk_threads_enter ();
|
||||
|
@ -75,12 +41,10 @@ void *Listen() {
|
|||
gtk_widget_set_sensitive (gui.button_clear, true);
|
||||
gdk_threads_leave ();
|
||||
|
||||
HedrShift = 0;
|
||||
PcmPointer = 0;
|
||||
Rate = 44100;
|
||||
snd_pcm_prepare(pcm_handle);
|
||||
snd_pcm_start (pcm_handle);
|
||||
Abort = false;
|
||||
Abort = false;
|
||||
|
||||
do {
|
||||
|
||||
|
@ -90,25 +54,44 @@ void *Listen() {
|
|||
// 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();
|
||||
PcmPointer = 0;
|
||||
snd_pcm_prepare(pcm_handle);
|
||||
snd_pcm_start (pcm_handle);
|
||||
}
|
||||
|
||||
} while (Mode == 0);
|
||||
|
||||
printf(" ==== %s ====\n", ModeSpec[Mode].Name);
|
||||
// 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(timestr, sizeof(timestr)-1,"%Y%m%d-%H%M%Sz", timeptr);
|
||||
strftime(CurrentPic.timestr, sizeof(CurrentPic.timestr)-1,"%Y%m%d-%H%M%Sz", timeptr);
|
||||
|
||||
|
||||
// Allocate space for cached Lum
|
||||
StoredLum = calloc( (int)(ModeSpec[Mode].LineLen * ModeSpec[Mode].ImgHeight + 1) * 44100, sizeof(guchar));
|
||||
free(StoredLum);
|
||||
StoredLum = calloc( (int)(ModeSpec[CurrentPic.Mode].LineLen * ModeSpec[CurrentPic.Mode].ImgHeight + 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[Mode].LineLen * ModeSpec[Mode].ImgHeight / SYNCPIXLEN +1), sizeof(bool));
|
||||
HasSync = calloc((int)(ModeSpec[CurrentPic.Mode].LineLen * ModeSpec[CurrentPic.Mode].ImgHeight / SYNCPIXLEN +1), sizeof(bool));
|
||||
if (HasSync == NULL) {
|
||||
perror("Listen: Unable to allocate memory for sync signal");
|
||||
exit(EXIT_FAILURE);
|
||||
|
@ -125,12 +108,12 @@ void *Listen() {
|
|||
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[Mode].Name);
|
||||
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, HedrShift);
|
||||
printf(" getvideo @ %.1f Hz, Skip %d, HedrShift %+d Hz\n", 44100.0, 0, CurrentPic.HedrShift);
|
||||
|
||||
Finished = GetVideo(Mode, 44100, 0, false);
|
||||
Finished = GetVideo(CurrentPic.Mode, 44100, 0, false);
|
||||
|
||||
gdk_threads_enter ();
|
||||
gtk_widget_set_sensitive (gui.button_abort, false);
|
||||
|
@ -159,38 +142,28 @@ void *Listen() {
|
|||
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",Rate);
|
||||
Rate = FindSync(Mode, Rate, &Skip);
|
||||
printf(" FindSync @ %.1f Hz\n",CurrentPic.Rate);
|
||||
CurrentPic.Rate = FindSync(CurrentPic.Mode, CurrentPic.Rate, &CurrentPic.Skip);
|
||||
|
||||
// Final image
|
||||
gdk_threads_enter ();
|
||||
gtk_statusbar_push (GTK_STATUSBAR(gui.statusbar), 0, "Redrawing..." );
|
||||
gdk_threads_leave ();
|
||||
printf(" getvideo @ %.1f Hz, Skip %d, HedrShift %+d Hz\n", Rate, Skip, HedrShift);
|
||||
GetVideo(Mode, Rate, Skip, true);
|
||||
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
|
||||
thumbbuf = gdk_pixbuf_scale_simple (pixbuf_rx, 100,
|
||||
100.0/ModeSpec[Mode].ImgWidth * ModeSpec[Mode].ImgHeight * ModeSpec[Mode].YScale, GDK_INTERP_HYPER);
|
||||
CurrentPic.thumbbuf = gdk_pixbuf_scale_simple (pixbuf_rx, 100,
|
||||
100.0/ModeSpec[CurrentPic.Mode].ImgWidth * ModeSpec[CurrentPic.Mode].ImgHeight * ModeSpec[CurrentPic.Mode].YScale, GDK_INTERP_HYPER);
|
||||
gdk_threads_enter ();
|
||||
gtk_list_store_prepend (savedstore, &iter);
|
||||
gtk_list_store_set (savedstore, &iter, 0, thumbbuf, 1, id, -1);
|
||||
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))) {
|
||||
|
||||
pngfilename = g_string_new(g_key_file_get_string(keyfile,"slowrx","rxdir",NULL));
|
||||
g_string_append_printf(pngfilename, "/%s_%s.png", timestr, ModeSpec[Mode].ShortName);
|
||||
printf(" Saving to %s\n", pngfilename->str);
|
||||
|
||||
gdk_threads_enter ();
|
||||
gtk_statusbar_push (GTK_STATUSBAR(gui.statusbar), 0, "Saving..." );
|
||||
gdk_threads_leave ();
|
||||
|
||||
setVU(0,-100);
|
||||
|
||||
/*ensure_dir_exists("rx-lum");
|
||||
|
@ -200,20 +173,9 @@ void *Listen() {
|
|||
fwrite(StoredLum,1,(ModeSpec[Mode].LineLen * ModeSpec[Mode].ImgHeight) * 44100,LumFile);
|
||||
fclose(LumFile);*/
|
||||
|
||||
// Save the received image as PNG
|
||||
GdkPixbuf *scaledpb;
|
||||
scaledpb = gdk_pixbuf_scale_simple (pixbuf_rx, ModeSpec[Mode].ImgWidth,
|
||||
ModeSpec[Mode].ImgHeight * ModeSpec[Mode].YScale, GDK_INTERP_HYPER);
|
||||
|
||||
ensure_dir_exists(g_key_file_get_string(keyfile,"slowrx","rxdir",NULL));
|
||||
gdk_pixbuf_savev(scaledpb, pngfilename->str, "png", NULL, NULL, NULL);
|
||||
g_object_unref(scaledpb);
|
||||
g_string_free(pngfilename, true);
|
||||
saveCurrentPic();
|
||||
}
|
||||
|
||||
free(StoredLum);
|
||||
StoredLum = NULL;
|
||||
|
||||
|
||||
gdk_threads_enter ();
|
||||
gtk_widget_set_sensitive (gui.frame_slant, true);
|
||||
gtk_widget_set_sensitive (gui.frame_manual, true);
|
||||
|
@ -245,14 +207,32 @@ int main(int argc, char *argv[]) {
|
|||
confpath = g_string_new(confdir);
|
||||
g_string_append(confpath, "/slowrx.ini");
|
||||
|
||||
keyfile = g_key_file_new();
|
||||
if (g_key_file_load_from_file(keyfile, confpath->str, G_KEY_FILE_KEEP_COMMENTS, NULL)) {
|
||||
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(keyfile, "[slowrx]\ndevice=default", -1, G_KEY_FILE_NONE, NULL);
|
||||
g_key_file_load_from_data(config, "[slowrx]\ndevice=default", -1, G_KEY_FILE_NONE, NULL);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
memset(in, 0, sizeof(double) * 2048);
|
||||
memset(out, 0, sizeof(double) * 2048);
|
||||
|
||||
Plan1024 = fftw_plan_r2r_1d(1024, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||
Plan2048 = fftw_plan_r2r_1d(2048, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||
|
||||
createGUI();
|
||||
populateDeviceList();
|
||||
|
||||
|
@ -262,14 +242,17 @@ int main(int argc, char *argv[]) {
|
|||
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);
|
||||
}
|
||||
confdata = g_key_file_to_data(keyfile,keylen,NULL);
|
||||
fprintf(ConfFile,"%s",confdata);
|
||||
fwrite(confdata,1,(size_t)keylen,ConfFile);
|
||||
fclose(ConfFile);
|
||||
|
||||
g_object_unref(pixbuf_rx);
|
||||
free(StoredLum);
|
||||
fftw_free(in);
|
||||
fftw_free(out);
|
||||
|
||||
return (EXIT_SUCCESS);
|
||||
}
|
||||
|
|
|
@ -546,6 +546,7 @@
|
|||
<child>
|
||||
<object class="GtkFrame" id="frame_slant">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
|
@ -571,7 +572,7 @@
|
|||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Manual resync</property>
|
||||
<property name="label" translatable="yes">Manual slant adj.</property>
|
||||
<property name="use_markup">True</property>
|
||||
</object>
|
||||
</child>
|
||||
|
|
5
sync.c
5
sync.c
|
@ -79,7 +79,7 @@ double FindSync (guchar Mode, double Rate, int *Skip) {
|
|||
printf(" %.1f° (d=%d) @ %.1f Hz", slantAngle, dMost, Rate);
|
||||
|
||||
// Adjust sample rate
|
||||
Rate = Rate + tan(deg2rad(90 - slantAngle)) / LineWidth * Rate;
|
||||
Rate += tan(deg2rad(90 - slantAngle)) / LineWidth * Rate;
|
||||
|
||||
if (slantAngle > 89 && slantAngle < 91) {
|
||||
printf(" slant OK :)\n");
|
||||
|
@ -113,7 +113,8 @@ double FindSync (guchar Mode, double Rate, int *Skip) {
|
|||
}
|
||||
}
|
||||
|
||||
// If pulse is on the right side, it just probably slipped out the left edge
|
||||
// If pulse is near the right edge of the iamge, it just probably slipped
|
||||
// out the left edge
|
||||
if (xmax > 350) xmax -= 350;
|
||||
|
||||
// Skip until the start of the line.
|
||||
|
|
20
video.c
20
video.c
|
@ -98,7 +98,7 @@ bool GetVideo(guchar Mode, double Rate, int Skip, bool Redraw) {
|
|||
gdk_threads_leave();
|
||||
|
||||
Length = ModeSpec[Mode].LineLen * ModeSpec[Mode].ImgHeight * 44100;
|
||||
SyncTargetBin = GetBin(1200+HedrShift, FFTLen);
|
||||
SyncTargetBin = GetBin(1200+CurrentPic.HedrShift, FFTLen);
|
||||
LopassBin = GetBin(5000, FFTLen);
|
||||
Abort = false;
|
||||
SyncSampleNum = 0;
|
||||
|
@ -132,14 +132,14 @@ bool GetVideo(guchar Mode, double Rate, int Skip, bool Redraw) {
|
|||
fftw_execute(Plan1024);
|
||||
|
||||
for (i=0;i<LopassBin;i++) {
|
||||
if (i >= GetBin(1500+HedrShift, FFTLen) && i <= GetBin(2300+HedrShift, FFTLen))
|
||||
if (i >= GetBin(1500+CurrentPic.HedrShift, FFTLen) && i <= GetBin(2300+CurrentPic.HedrShift, FFTLen))
|
||||
Praw += pow(out[i], 2) + pow(out[FFTLen-i], 2);
|
||||
|
||||
if (i >= SyncTargetBin-1 && i <= SyncTargetBin+1)
|
||||
Psync += (pow(out[i], 2) + pow(out[FFTLen-i], 2)) * (1- .5*abs(SyncTargetBin-i));
|
||||
}
|
||||
|
||||
Praw /= (GetBin(2300+HedrShift, FFTLen) - GetBin(1500+HedrShift, FFTLen));
|
||||
Praw /= (GetBin(2300+CurrentPic.HedrShift, FFTLen) - GetBin(1500+CurrentPic.HedrShift, FFTLen));
|
||||
Psync /= 2.0;
|
||||
|
||||
// If there is more than twice the amount of power per Hz in the
|
||||
|
@ -166,16 +166,16 @@ bool GetVideo(guchar Mode, double Rate, int Skip, bool Redraw) {
|
|||
// Calculate video-plus-noise power (1500-2300 Hz)
|
||||
|
||||
Pvideo_plus_noise = 0;
|
||||
for (n = GetBin(1500+HedrShift, FFTLen); n <= GetBin(2300+HedrShift, FFTLen); n++)
|
||||
for (n = GetBin(1500+CurrentPic.HedrShift, FFTLen); n <= GetBin(2300+CurrentPic.HedrShift, FFTLen); n++)
|
||||
Pvideo_plus_noise += pow(out[n], 2) + pow(out[FFTLen - n], 2);
|
||||
|
||||
// Calculate noise-only power (400-800 Hz + 2700-3400 Hz)
|
||||
|
||||
Pnoise_only = 0;
|
||||
for (n = GetBin(400+HedrShift, FFTLen); n <= GetBin(800+HedrShift, FFTLen); n++)
|
||||
for (n = GetBin(400+CurrentPic.HedrShift, FFTLen); n <= GetBin(800+CurrentPic.HedrShift, FFTLen); n++)
|
||||
Pnoise_only += pow(out[n], 2) + pow(out[FFTLen - n], 2);
|
||||
|
||||
for (n = GetBin(2700+HedrShift, FFTLen); n <= GetBin(3400+HedrShift, FFTLen); n++)
|
||||
for (n = GetBin(2700+CurrentPic.HedrShift, FFTLen); n <= GetBin(3400+CurrentPic.HedrShift, FFTLen); n++)
|
||||
Pnoise_only += pow(out[n], 2) + pow(out[FFTLen - n], 2);
|
||||
|
||||
// Bandwidths
|
||||
|
@ -231,7 +231,7 @@ bool GetVideo(guchar Mode, double Rate, int Skip, bool Redraw) {
|
|||
MaxBin = 0;
|
||||
|
||||
// Find the bin with most power
|
||||
for (n = GetBin(1500 + HedrShift, FFTLen) - 1; n <= GetBin(2300 + HedrShift, FFTLen) + 1; n++) {
|
||||
for (n = GetBin(1500 + CurrentPic.HedrShift, FFTLen) - 1; n <= GetBin(2300 + CurrentPic.HedrShift, FFTLen) + 1; n++) {
|
||||
|
||||
Power[n] = pow(out[n],2) + pow(out[FFTLen - n], 2);
|
||||
if (MaxBin == 0 || Power[n] > Power[MaxBin]) MaxBin = n;
|
||||
|
@ -239,7 +239,7 @@ bool GetVideo(guchar Mode, double Rate, int Skip, bool Redraw) {
|
|||
}
|
||||
|
||||
// Find the peak frequency by Gaussian interpolation
|
||||
if (MaxBin > GetBin(1500 + HedrShift, FFTLen) - 1 && MaxBin < GetBin(2300 + HedrShift, FFTLen) + 1) {
|
||||
if (MaxBin > GetBin(1500 + CurrentPic.HedrShift, FFTLen) - 1 && MaxBin < GetBin(2300 + CurrentPic.HedrShift, FFTLen) + 1) {
|
||||
Freq = MaxBin + (log( Power[MaxBin + 1] / Power[MaxBin - 1] )) /
|
||||
(2 * log( pow(Power[MaxBin], 2) / (Power[MaxBin + 1] * Power[MaxBin - 1])));
|
||||
// In Hertz
|
||||
|
@ -247,7 +247,7 @@ bool GetVideo(guchar Mode, double Rate, int Skip, bool Redraw) {
|
|||
InterpFreq = Freq;
|
||||
} else {
|
||||
// Clip if out of bounds
|
||||
Freq = ( (MaxBin > GetBin(1900 + HedrShift, FFTLen)) ? 2300 : 1500 ) + HedrShift;
|
||||
Freq = ( (MaxBin > GetBin(1900 + CurrentPic.HedrShift, FFTLen)) ? 2300 : 1500 ) + CurrentPic.HedrShift;
|
||||
}
|
||||
|
||||
NextFFTtime += 0.3e-3;
|
||||
|
@ -258,7 +258,7 @@ bool GetVideo(guchar Mode, double Rate, int Skip, bool Redraw) {
|
|||
InterpFreq = PrevFreq + (t - NextFFTtime + 0.6e-3) * ((Freq - PrevFreq) / 0.3e-3);
|
||||
|
||||
// Calculate luminency & store for later use
|
||||
StoredLum[SampleNum] = clip((InterpFreq - (1500 + HedrShift)) / 3.1372549);
|
||||
StoredLum[SampleNum] = clip((InterpFreq - (1500 + CurrentPic.HedrShift)) / 3.1372549);
|
||||
|
||||
}
|
||||
|
||||
|
|
22
vis.c
22
vis.c
|
@ -37,12 +37,12 @@ guchar GetVIS () {
|
|||
printf("Waiting for header\n");
|
||||
|
||||
gdk_threads_enter();
|
||||
gtk_statusbar_push( GTK_STATUSBAR(gui.statusbar), 0, "Ready" );
|
||||
gtk_statusbar_push( GTK_STATUSBAR(gui.statusbar), 0, "Listening" );
|
||||
gdk_threads_leave();
|
||||
|
||||
while ( true ) {
|
||||
|
||||
if (Abort) return(0);
|
||||
if (Abort || ManualResync) return(0);
|
||||
|
||||
// Read 10 ms from sound card
|
||||
readPcm(441);
|
||||
|
@ -77,10 +77,10 @@ guchar GetVIS () {
|
|||
|
||||
// Is there a pattern that looks like (the end of) a calibration header + VIS?
|
||||
// Tolerance ±25 Hz
|
||||
HedrShift = 0;
|
||||
CurrentPic.HedrShift = 0;
|
||||
gotvis = false;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (HedrShift != 0) break;
|
||||
if (CurrentPic.HedrShift != 0) break;
|
||||
for (j = 0; j < 3; j++) {
|
||||
if ( (tone[1*3+i] > tone[0+j] - 25 && tone[1*3+i] < tone[0+j] + 25) && // 1900 Hz leader
|
||||
(tone[2*3+i] > tone[0+j] - 25 && tone[2*3+i] < tone[0+j] + 25) && // 1900 Hz leader
|
||||
|
@ -103,13 +103,13 @@ guchar GetVIS () {
|
|||
}
|
||||
}
|
||||
if (gotvis) {
|
||||
HedrShift = tone[0+j] - 1900;
|
||||
CurrentPic.HedrShift = tone[0+j] - 1900;
|
||||
|
||||
VIS = Bit[0] + (Bit[1] << 1) + (Bit[2] << 2) + (Bit[3] << 3) + (Bit[4] << 4) +
|
||||
(Bit[5] << 5) + (Bit[6] << 6);
|
||||
ParityBit = Bit[7];
|
||||
|
||||
printf(" VIS %d (%02Xh) @ %+d Hz\n", VIS, VIS, HedrShift);
|
||||
printf(" VIS %d (%02Xh) @ %+d Hz\n", VIS, VIS, CurrentPic.HedrShift);
|
||||
|
||||
Parity = Bit[0] ^ Bit[1] ^ Bit[2] ^ Bit[3] ^ Bit[4] ^ Bit[5] ^ Bit[6];
|
||||
|
||||
|
@ -124,7 +124,7 @@ guchar GetVIS () {
|
|||
} else {
|
||||
gdk_threads_enter();
|
||||
gtk_combo_box_set_active (GTK_COMBO_BOX(gui.combo_mode), VISmap[VIS]-1);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON(gui.spin_shift), HedrShift);
|
||||
gtk_spin_button_set_value (GTK_SPIN_BUTTON(gui.spin_shift), CurrentPic.HedrShift);
|
||||
gdk_threads_leave();
|
||||
break;
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ guchar GetVIS () {
|
|||
gdk_threads_leave();
|
||||
|
||||
selmode = gtk_combo_box_get_active (GTK_COMBO_BOX(gui.combo_mode)) + 1;
|
||||
HedrShift = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(gui.spin_shift));
|
||||
CurrentPic.HedrShift = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(gui.spin_shift));
|
||||
VIS = 0;
|
||||
for (i=0; i<0x80; i++) {
|
||||
if (VISmap[i] == selmode) {
|
||||
|
@ -170,12 +170,6 @@ guchar GetVIS () {
|
|||
readPcm(20e-3 * 44100);
|
||||
PcmPointer += 20e-3 * 44100;
|
||||
|
||||
// In case of Scottie, skip first sync pulse
|
||||
/*if (VISmap[VIS] == S1 || VISmap[VIS] == S2 || VISmap[VIS] == SDX) {
|
||||
readPcm(ModeSpec[VISmap[VIS]].SyncLen * 44100);
|
||||
PcmPointer += ModeSpec[VISmap[VIS]].SyncLen * 44100;
|
||||
}*/
|
||||
|
||||
if (VISmap[VIS] != UNKNOWN) return VISmap[VIS];
|
||||
else printf(" No VIS found\n");
|
||||
return 0;
|
||||
|
|
Ładowanie…
Reference in New Issue