From 0b0d1ad529b554fde44c633ab72743964b6126b5 Mon Sep 17 00:00:00 2001 From: c vw Date: Fri, 8 Jun 2018 12:13:55 +0200 Subject: [PATCH] Port to MacOS, Compiler warnings removed, thus found many errors. Includes two fixes for STEMlab/HAMlab. --- MacOS/Info.plist | 16 ++ MacOS/PkgInfo | 1 + MacOS/hpsdr.icns | Bin 0 -> 98616 bytes MacOS/hpsdr.png | Bin 0 -> 15116 bytes MacOS/pihpsdr.sh | 14 ++ Makefile | 101 +++++++++- README.MacOS | 141 ++++++++++++++ agc_menu.c | 4 + audio.c | 8 +- audio_waterfall.c | 9 +- band_menu.c | 4 + bandstack_menu.c | 4 + dsp_menu.c | 3 +- equalizer_menu.c | 12 ++ error_handler.c | 4 + ext.c | 10 + filter_menu.c | 8 + freqent_menu.c | 2 + iambic.c | 17 ++ main.c | 45 +++-- mode_menu.c | 4 + new_discovery.c | 5 +- new_protocol.c | 165 +++++++++++++++- new_protocol.h | 4 + new_protocol_programmer.c | 16 +- old_discovery.c | 6 +- old_protocol.c | 122 ++++++++++-- portaudio.c | 396 ++++++++++++++++++++++++++++++++++++++ radio.c | 32 ++- receiver.c | 3 +- receiver.h | 11 +- rigctl.c | 35 ++-- rx_menu.c | 15 +- stemlab_discovery.c | 203 +++++++++++++++++++ step_menu.c | 4 + store_menu.c | 8 + transmitter.c | 2 + tx_menu.c | 4 + tx_panadapter.c | 7 +- vfo.c | 4 + vfo_menu.c | 4 + vox_menu.c | 2 + waterfall.c | 9 +- 43 files changed, 1389 insertions(+), 75 deletions(-) create mode 100644 MacOS/Info.plist create mode 100644 MacOS/PkgInfo create mode 100644 MacOS/hpsdr.icns create mode 100755 MacOS/hpsdr.png create mode 100755 MacOS/pihpsdr.sh create mode 100644 README.MacOS create mode 100644 portaudio.c diff --git a/MacOS/Info.plist b/MacOS/Info.plist new file mode 100644 index 00000000..9bca9687 --- /dev/null +++ b/MacOS/Info.plist @@ -0,0 +1,16 @@ + + + + + CFBundleIdentifier + piHPSDR + CFBundleExecutable + pihpsdr + CFBundleIconFile + hpsdr.icns + CFBundlePackageType + APPL + CFBundleSignature + BNDL + + diff --git a/MacOS/PkgInfo b/MacOS/PkgInfo new file mode 100644 index 00000000..610abe05 --- /dev/null +++ b/MacOS/PkgInfo @@ -0,0 +1 @@ +APPLpihp diff --git a/MacOS/hpsdr.icns b/MacOS/hpsdr.icns new file mode 100644 index 0000000000000000000000000000000000000000..4d1f6a8c7412f4cecca910ba3a38841e5e6fd9ee GIT binary patch literal 98616 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 U7?>&97mLN;qe7!0FhoND0Jy5qo&W#< literal 0 HcmV?d00001 diff --git a/MacOS/hpsdr.png b/MacOS/hpsdr.png new file mode 100755 index 0000000000000000000000000000000000000000..ea96fd7a497da5a35c56bef1c6e4636163882aed GIT binary patch literal 15116 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 vfo_update(); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } void agc_menu(GtkWidget *parent) { diff --git a/audio.c b/audio.c index 923685aa..f6a74392 100644 --- a/audio.c +++ b/audio.c @@ -17,6 +17,11 @@ * */ +// +// DL1YCF: If PortAudio is used instead of ALSO (e.g. on MacOS), +// this file is not used (and replaced by portaudio.c). + +#ifndef PORTAUDIO #include @@ -527,4 +532,5 @@ fprintf(stderr,"output_device: %s\n",device_id); n++; } snd_device_name_free_hint(hints); -} \ No newline at end of file +} +#endif diff --git a/audio_waterfall.c b/audio_waterfall.c index 48ed0b9c..cbf606f7 100644 --- a/audio_waterfall.c +++ b/audio_waterfall.c @@ -78,7 +78,8 @@ waterfall_configure_event_cb (GtkWidget *widget, int height=gtk_widget_get_allocated_height (widget); fprintf(stderr,"audio: waterfall_configure_event: width=%d height=%d\n",width,height); pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height); - char *pixels = gdk_pixbuf_get_pixels (pixbuf); + // DL1YCF changed to uchar * + unsigned char *pixels = gdk_pixbuf_get_pixels (pixbuf); memset(pixels, 0, width*height*3); @@ -135,7 +136,8 @@ void audio_waterfall_update() { int i; if(pixbuf) { - char *pixels = gdk_pixbuf_get_pixels (pixbuf); + // DL1YCF changed to uchar * + unsigned char *pixels = gdk_pixbuf_get_pixels (pixbuf); int width=gdk_pixbuf_get_width(pixbuf); int height=gdk_pixbuf_get_height(pixbuf); @@ -147,7 +149,8 @@ void audio_waterfall_update() { memmove(&pixels[rowstride*(header+1)],&pixels[rowstride*header],(height-(header+1))*rowstride); float sample; - char *p; + // DL1YCF changed to uchar * + unsigned char *p; int average=0; p=&pixels[rowstride*header]; for(i=0;inr2_gain_method==(uintptr_t)data; +// DL1YCF changed == to = + active_receiver->nr2_gain_method=(uintptr_t)data; SetRXAEMNRgainMethod(active_receiver->id, active_receiver->nr2_gain_method); } diff --git a/equalizer_menu.c b/equalizer_menu.c index 514e77d6..325c2492 100644 --- a/equalizer_menu.c +++ b/equalizer_menu.c @@ -70,6 +70,10 @@ static gboolean tx_rb_cb (GtkWidget *widget, GdkEventButton *event, gpointer dat gtk_range_set_value(GTK_RANGE(mid_scale),(double)tx_equalizer[2]); gtk_range_set_value(GTK_RANGE(high_scale),(double)tx_equalizer[3]); } + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } static gboolean rx_rb_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) { @@ -82,6 +86,10 @@ static gboolean rx_rb_cb (GtkWidget *widget, GdkEventButton *event, gpointer dat gtk_range_set_value(GTK_RANGE(mid_scale),(double)rx_equalizer[2]); gtk_range_set_value(GTK_RANGE(high_scale),(double)rx_equalizer[3]); } + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } static gboolean enable_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) { @@ -92,6 +100,10 @@ static gboolean enable_cb (GtkWidget *widget, GdkEventButton *event, gpointer da enable_rx_equalizer=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); SetRXAEQRun(active_receiver->id, enable_rx_equalizer); } + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } static void value_changed_cb (GtkWidget *widget, gpointer data) { diff --git a/error_handler.c b/error_handler.c index 5c7313e6..800b04d1 100644 --- a/error_handler.c +++ b/error_handler.c @@ -22,6 +22,10 @@ int show_error(void *data) { gtk_widget_show(label); timer=g_timeout_add(5000,timeout_cb,NULL); int result=gtk_dialog_run(GTK_DIALOG(dialog)); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } void error_handler(char *text,char *err) { diff --git a/ext.c b/ext.c index 071736bb..c5ba6191 100644 --- a/ext.c +++ b/ext.c @@ -44,6 +44,10 @@ int ext_discovery(void *data) { int ext_set_frequency(void *data) { setFrequency(*(long long *)data); free(data); + // DL1YCF added return statement + // this one is CRITICAL to avoid free() being called + // repeatedly on the same pointer + return 0; } int ext_vfo_update(void *data) { @@ -67,14 +71,20 @@ int ext_band_update(void *data) { int ext_mode_update(void *data) { start_mode(); + // DL1YCF added return statement + return 0; } int ext_filter_update(void *data) { start_filter(); + // DL1YCF added return statement + return 0; } int ext_noise_update(void *data) { start_noise(); + // DL1YCF added return statement + return 0; } int ext_ptt_update(void *data) { diff --git a/filter_menu.c b/filter_menu.c index aee2b684..092d8906 100644 --- a/filter_menu.c +++ b/filter_menu.c @@ -67,6 +67,10 @@ static gboolean filter_select_cb (GtkWidget *widget, gpointer data) { set_button_text_color(last_filter,"black"); last_filter=widget; set_button_text_color(last_filter,"orange"); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } static gboolean deviation_select_cb (GtkWidget *widget, gpointer data) { @@ -87,6 +91,10 @@ static gboolean deviation_select_cb (GtkWidget *widget, gpointer data) { last_filter=widget; set_button_text_color(last_filter,"orange"); vfo_update(); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } static void var_spin_low_cb (GtkWidget *widget, gpointer data) { diff --git a/freqent_menu.c b/freqent_menu.c index 8028ad0f..0370ff72 100644 --- a/freqent_menu.c +++ b/freqent_menu.c @@ -146,6 +146,8 @@ static gboolean freqent_select_cb (GtkWidget *widget, gpointer data) { } } vfo_update(); + // DL1YCF added return statement + return FALSE; } static GtkWidget *last_mode; diff --git a/iambic.c b/iambic.c index 51026b56..71f25029 100644 --- a/iambic.c +++ b/iambic.c @@ -116,7 +116,11 @@ static int kcwr = 0; int *kdot; int *kdash; static int running = 0; +#ifdef __APPLE__ +static sem_t *cw_event; +#else static sem_t cw_event; +#endif int keyer_out = 0; @@ -160,7 +164,11 @@ void keyer_event(int gpio, int level) { kcwr = state; if (state || cw_keyer_mode == KEYER_STRAIGHT) { +#ifdef __APPLE__ + sem_post(cw_event); +#else sem_post(&cw_event); +#endif } } #endif @@ -197,7 +205,11 @@ static void* keyer_thread(void *arg) { fprintf(stderr,"keyer_thread state running= %d\n", running); while(running) { +#ifdef __APPLE__ + sem_wait(cw_event); +#else sem_wait(&cw_event); +#endif key_state = CHECK; @@ -390,7 +402,12 @@ int keyer_init() { softToneCreate(SIDETONE_GPIO); } +#ifdef __APPLE__ + cw_event=sem_open("CW", O_CREAT, 0700, 0); + rc = (cw_event == SEM_FAILED); +#else rc = sem_init(&cw_event, 0, 0); +#endif rc |= pthread_create(&keyer_thread_id, NULL, keyer_thread, NULL); running = 1; if(rc < 0) { diff --git a/main.c b/main.c index b17a210a..4e3bcbca 100644 --- a/main.c +++ b/main.c @@ -17,6 +17,13 @@ * */ +// DL1YCF +// Define maximum window size. +// Original values 800 and 480, but if the screen is large, why not using it? + +#define MAX_DISPLAY_WIDTH 1020 +#define MAX_DISPLAY_HEIGHT 700 + #include #include #include @@ -58,7 +65,11 @@ gint full_screen=1; static GtkWidget *discovery_dialog; +#ifdef __APPLE__ +static sem_t *wisdom_sem; +#else static sem_t wisdom_sem; +#endif static GdkCursor *cursor_arrow; static GdkCursor *cursor_watch; @@ -91,7 +102,12 @@ static void* wisdom_thread(void *arg) { fprintf(stderr,"Creating wisdom file: %s\n", (char *)arg); status_text("Creating FFTW Wisdom file ..."); WDSPwisdom ((char *)arg); +#ifdef __APPLE__ + sem_post(wisdom_sem); +#else sem_post(&wisdom_sem); +#endif + return NULL; } gboolean main_delete (GtkWidget *widget) { @@ -138,9 +154,18 @@ static int init(void *data) { strcpy(&wisdom_file[strlen(wisdom_file)],"wdspWisdom"); status_text("Checking FFTW Wisdom file ..."); if(access(wisdom_file,F_OK)<0) { +#ifdef __APPLE__ + int rc; + wisdom_sem=sem_open("WISDOM", O_CREAT, 0700, 0); +#else int rc=sem_init(&wisdom_sem, 0, 0); +#endif rc=pthread_create(&wisdom_thread_id, NULL, wisdom_thread, (void *)wisdom_directory); +#ifdef __APPLE__ + while(sem_trywait(wisdom_sem)<0) { +#else while(sem_trywait(&wisdom_sem)<0) { +#endif status_text(wisdom_get_status()); while (gtk_events_pending ()) gtk_main_iteration (); @@ -178,21 +203,11 @@ static void activate_pihpsdr(GtkApplication *app, gpointer data) { display_height=gdk_screen_get_height(screen); fprintf(stderr,"width=%d height=%d\n", display_width, display_height); - if(display_width>800 || display_height>480) { -/* - if(display_width>1600) { - display_width=1600; - } else { - display_width=800; - } - if(display_height>960) { - display_height=960; - } else { - display_height=480; - } -*/ - display_width=800; - display_height=480; + + // DL1YCF: use define'd constants here + if(display_width>MAX_DISPLAY_WIDTH || display_height>MAX_DISPLAY_HEIGHT) { + display_width=MAX_DISPLAY_WIDTH; + display_height=MAX_DISPLAY_HEIGHT; full_screen=0; } diff --git a/mode_menu.c b/mode_menu.c index b2c02bb3..d40eee33 100644 --- a/mode_menu.c +++ b/mode_menu.c @@ -64,6 +64,10 @@ static gboolean mode_select_cb (GtkWidget *widget, gpointer data) { last_mode=widget; set_button_text_color(last_mode,"orange"); vfo_mode_changed(m); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } void mode_menu(GtkWidget *parent) { diff --git a/new_discovery.c b/new_discovery.c index 8ab5f888..634d99d3 100644 --- a/new_discovery.c +++ b/new_discovery.c @@ -185,7 +185,8 @@ void new_discover(struct ifaddrs* iface) { //void* new_discover_receive_thread(void* arg) { gpointer new_discover_receive_thread(gpointer data) { struct sockaddr_in addr; - int len; + // DL1YCF change from int to socklen_t + socklen_t len; unsigned char buffer[2048]; int bytes_read; struct timeval tv; @@ -275,4 +276,6 @@ gpointer new_discover_receive_thread(gpointer data) { } fprintf(stderr,"new_discover: exiting new_discover_receive_thread\n"); g_thread_exit(NULL); + // DL1YCF added return statement to make compiler happy. + return NULL; } diff --git a/new_protocol.c b/new_protocol.c index 78da6c8e..93d84a9a 100644 --- a/new_protocol.c +++ b/new_protocol.c @@ -73,7 +73,18 @@ int data_socket=-1; static int running; +#ifdef __APPLE__ +// +//DL1YCF: +//Mac OS does not have sem_init for un-named semaphores, +//we have to use named semaphores created with sem_open. +//As a side effect, we consistently have to use sem_t +//pointers instead of sem_t variables +// +sem_t *response_sem; +#else sem_t response_sem; +#endif static struct sockaddr_in base_addr; static int base_addr_length; @@ -137,17 +148,37 @@ static int response; //static sem_t send_general_sem; //static int send_general=0; +#ifdef __APPLE__ +static sem_t *command_response_sem_ready; +static sem_t *command_response_sem_buffer; +#else static sem_t command_response_sem_ready; static sem_t command_response_sem_buffer; +#endif static GThread *command_response_thread_id; +#ifdef __APPLE__ +static sem_t *high_priority_sem_ready; +static sem_t *high_priority_sem_buffer; +#else static sem_t high_priority_sem_ready; static sem_t high_priority_sem_buffer; +#endif static GThread *high_priority_thread_id; +#ifdef __APPLE__ +static sem_t *mic_line_sem_ready; +static sem_t *mic_line_sem_buffer; +#else static sem_t mic_line_sem_ready; static sem_t mic_line_sem_buffer; +#endif static GThread *mic_line_thread_id; +#ifdef __APPLE__ +static sem_t *iq_sem_ready[MAX_RECEIVERS]; +static sem_t *iq_sem_buffer[MAX_RECEIVERS]; +#else static sem_t iq_sem_ready[MAX_RECEIVERS]; static sem_t iq_sem_buffer[MAX_RECEIVERS]; +#endif static GThread *iq_thread_id[MAX_RECEIVERS]; static int samples[MAX_RECEIVERS]; @@ -169,7 +200,8 @@ static int psk_resample=6; // convert from 48000 to 8000 #define NET_BUFFER_SIZE 2048 // Network buffers static struct sockaddr_in addr; -static int length; +// DL1YCF next line: changed int to socklen_t +static socklen_t length; //static unsigned char buffer[NET_BUFFER_SIZE]; static unsigned char *iq_buffer[MAX_RECEIVERS]; static unsigned char *command_response_buffer; @@ -296,28 +328,47 @@ void new_protocol_init(int pixels) { new_protocol_calc_buffers(); #endif +#ifdef __APPLE__ + response_sem=sem_open("RESPONSE", O_CREAT, 0700, 0); +#else rc=sem_init(&response_sem, 0, 0); +#endif //rc=sem_init(&send_high_priority_sem, 0, 1); //rc=sem_init(&send_general_sem, 0, 1); +#ifdef __APPLE__ + command_response_sem_ready=sem_open("COMMRESREADY", O_CREAT, 0700, 0); + command_response_sem_buffer=sem_open("COMMRESBUF", O_CREAT, 0700, 0); +#else rc=sem_init(&command_response_sem_ready, 0, 0); rc=sem_init(&command_response_sem_buffer, 0, 0); +#endif command_response_thread_id = g_thread_new( "command_response thread",command_response_thread, NULL); if( ! command_response_thread_id ) { fprintf(stderr,"g_thread_new failed on command_response_thread\n"); exit( -1 ); } fprintf(stderr, "command_response_thread: id=%p\n",command_response_thread_id); +#ifdef __APPLE__ + high_priority_sem_ready=sem_open("HIGHREADY", O_CREAT, 0700, 0); + high_priority_sem_buffer=sem_open("HIGHBUF", O_CREAT, 0700, 0); +#else rc=sem_init(&high_priority_sem_ready, 0, 0); rc=sem_init(&high_priority_sem_buffer, 0, 0); +#endif high_priority_thread_id = g_thread_new( "high_priority thread", high_priority_thread, NULL); if( ! high_priority_thread_id ) { fprintf(stderr,"g_thread_new failed on high_priority_thread\n"); exit( -1 ); } fprintf(stderr, "high_priority_thread: id=%p\n",high_priority_thread_id); +#ifdef __APPLE__ + mic_line_sem_ready=sem_open("MICREADY", O_CREAT, 0700, 0); + mic_line_sem_buffer=sem_open("MICBUF", O_CREAT, 0700, 0); +#else rc=sem_init(&mic_line_sem_ready, 0, 0); rc=sem_init(&mic_line_sem_buffer, 0, 0); +#endif mic_line_thread_id = g_thread_new( "mic_line thread", mic_line_thread, NULL); if( ! mic_line_thread_id ) { fprintf(stderr,"g_thread_new failed on mic_line_thread\n"); @@ -326,23 +377,45 @@ void new_protocol_init(int pixels) { fprintf(stderr, "mic_line_thread: id=%p\n",mic_line_thread_id); for(i=0;iddc; +#ifdef __APPLE__ + sprintf(sname,"IQREADY%03d", ddc); + iq_sem_ready[ddc]=sem_open(sname, O_CREAT, 0700, 0); + sprintf(sname,"IQBUF%03d", ddc); + iq_sem_buffer[ddc]=sem_open(sname, O_CREAT, 0700, 0); +#else rc=sem_init(&iq_sem_ready[ddc], 0, 0); rc=sem_init(&iq_sem_buffer[ddc], 0, 0); +#endif iq_thread_id[ddc] = g_thread_new( "iq thread", iq_thread, (gpointer)(long)i); - if( ! iq_thread_id ) { - fprintf(stderr,"g_thread_new failed for iq_thread: rx=%d\n",i); - exit( -1 ); - } + //DL1YCF: g_thread new always returns a value, upon failure the program aborts + // so the next four lines have been deactivated. + //if( ! iq_thread_id ) { + // fprintf(stderr,"g_thread_new failed for iq_thread: rx=%d\n",i); + // exit( -1 ); + //} fprintf(stderr, "iq_thread: rx=%d ddc=%d thread=%p\n",i, ddc, iq_thread_id); } #ifdef PURESIGNAL // for PS the two feedback streams are synced on the one DDC if(device!=NEW_DEVICE_HERMES) { +#ifdef __APPLE__ + char sname[12]; +#endif ddc=receiver[PS_TX_FEEDBACK]->ddc; +#ifdef __APPLE__ + sprintf(sname,"IQREADY%03d", ddc); + iq_sem_ready[ddc]=sem_open(sname, O_CREAT, 0700, 0); + sprintf(sname,"IQBUF%03d", ddc); + iq_sem_buffer[ddc]=sem_open(sname, O_CREAT, 0700, 0); +#else rc=sem_init(&iq_sem_ready[ddc], 0, 0); rc=sem_init(&iq_sem_buffer[ddc], 0, 0); +#endif iq_thread_id[ddc] = g_thread_new( "ps iq thread", ps_iq_thread, (gpointer)(long)PS_TX_FEEDBACK); if( ! iq_thread_id ) { fprintf(stderr,"g_thread_new failed for ps_iq_thread: rx=%d\n",PS_TX_FEEDBACK); @@ -1144,28 +1217,60 @@ fprintf(stderr,"new_protocol_thread: high_priority_addr setup for port %d\n",HIG if(ddc>=MAX_RECEIVERS) { fprintf(stderr,"unexpected iq data from ddc %d\n",ddc); } else { +#ifdef __APPLE__ + sem_wait(iq_sem_ready[ddc]); +#else sem_wait(&iq_sem_ready[ddc]); +#endif iq_buffer[ddc]=buffer; +#ifdef __APPLE__ + sem_post(iq_sem_buffer[ddc]); +#else sem_post(&iq_sem_buffer[ddc]); +#endif } break; case COMMAND_RESPONCE_TO_HOST_PORT: +#ifdef __APPLE__ + sem_wait(command_response_sem_ready); +#else sem_wait(&command_response_sem_ready); +#endif command_response_buffer=buffer; +#ifdef __APPLE__ + sem_post(command_response_sem_buffer); +#else sem_post(&command_response_sem_buffer); +#endif //process_command_response(); break; case HIGH_PRIORITY_TO_HOST_PORT: +#ifdef __APPLE__ + sem_wait(high_priority_sem_ready); +#else sem_wait(&high_priority_sem_ready); +#endif high_priority_buffer=buffer; +#ifdef __APPLE__ + sem_post(high_priority_sem_buffer); +#else sem_post(&high_priority_sem_buffer); +#endif //process_high_priority(); break; case MIC_LINE_TO_HOST_PORT: +#ifdef __APPLE__ + sem_wait(mic_line_sem_ready); +#else sem_wait(&mic_line_sem_ready); +#endif mic_line_buffer=buffer; mic_bytes_read=bytesread; +#ifdef __APPLE__ + sem_post(mic_line_sem_buffer); +#else sem_post(&mic_line_sem_buffer); +#endif break; default: fprintf(stderr,"new_protocol_thread: Unknown port %d\n",sourceport); @@ -1175,13 +1280,19 @@ fprintf(stderr,"new_protocol_thread: Unknown port %d\n",sourceport); } close(data_socket); + return NULL; } static gpointer command_response_thread(gpointer data) { while(1) { fprintf(stderr,"command_response_thread\n"); +#ifdef __APPLE__ + sem_post(command_response_sem_ready); + sem_wait(command_response_sem_buffer); +#else sem_post(&command_response_sem_ready); sem_wait(&command_response_sem_buffer); +#endif process_command_response(); free(command_response_buffer); } @@ -1190,8 +1301,13 @@ fprintf(stderr,"command_response_thread\n"); static gpointer high_priority_thread(gpointer data) { fprintf(stderr,"high_priority_thread\n"); while(1) { +#ifdef __APPLE__ + sem_post(high_priority_sem_ready); + sem_wait(high_priority_sem_buffer); +#else sem_post(&high_priority_sem_ready); sem_wait(&high_priority_sem_buffer); +#endif process_high_priority(); free(high_priority_buffer); } @@ -1200,8 +1316,13 @@ fprintf(stderr,"high_priority_thread\n"); static gpointer mic_line_thread(gpointer data) { fprintf(stderr,"mic_line_thread\n"); while(1) { +#ifdef __APPLE__ + sem_post(mic_line_sem_ready); + sem_wait(mic_line_sem_buffer); +#else sem_post(&mic_line_sem_ready); sem_wait(&mic_line_sem_buffer); +#endif if(!transmitter->local_microphone) { process_mic_data(mic_bytes_read); } @@ -1214,8 +1335,13 @@ static gpointer iq_thread(gpointer data) { int ddc=receiver[rx]->ddc; fprintf(stderr,"iq_thread: rx=%d ddc=%d\n",rx,ddc); while(1) { +#ifdef __APPLE__ + sem_post(iq_sem_ready[ddc]); + sem_wait(iq_sem_buffer[ddc]); +#else sem_post(&iq_sem_ready[ddc]); sem_wait(&iq_sem_buffer[ddc]); +#endif process_iq_data(receiver[rx]); free(iq_buffer[ddc]); } @@ -1227,8 +1353,13 @@ static gpointer ps_iq_thread(gpointer data) { int ddc=receiver[rx]->ddc; fprintf(stderr,"ps_iq_thread: rx=%d ddc=%d\n",rx,ddc); while(1) { +#ifdef __APPLE__ + sem_post(iq_sem_ready[ddc]); + sem_wait(iq_sem_buffer[ddc]); +#else sem_post(&iq_sem_ready[ddc]); sem_wait(&iq_sem_buffer[ddc]); +#endif process_ps_iq_data(receiver[rx]); free(iq_buffer[ddc]); } @@ -1257,7 +1388,8 @@ static void process_iq_data(RECEIVER *rx) { } rx->iq_sequence++; - timestamp=((long long)(buffer[4]&0xFF)<<56)+((long long)(buffer[5]&0xFF)<<48)+((long long)(buffer[6]&0xFF)<<40)+((long long)(buffer[7]&0xFF)<<32); +// DL1YCF: changed semicolon at end of next line to a plus sign + timestamp=((long long)(buffer[4]&0xFF)<<56)+((long long)(buffer[5]&0xFF)<<48)+((long long)(buffer[6]&0xFF)<<40)+((long long)(buffer[7]&0xFF)<<32)+ ((long long)(buffer[8]&0xFF)<<24)+((long long)(buffer[9]&0xFF)<<16)+((long long)(buffer[10]&0xFF)<<8)+(long long)(buffer[11]&0xFF); bitspersample=((buffer[12]&0xFF)<<8)+(buffer[13]&0xFF); samplesperframe=((buffer[14]&0xFF)<<8)+(buffer[15]&0xFF); @@ -1378,7 +1510,11 @@ static void process_command_response() { response_sequence=((command_response_buffer[0]&0xFF)<<24)+((command_response_buffer[1]&0xFF)<<16)+((command_response_buffer[2]&0xFF)<<8)+(command_response_buffer[3]&0xFF); response=command_response_buffer[4]&0xFF; fprintf(stderr,"response_sequence=%ld response=%d\n",response_sequence,response); +#ifdef __APPLE__ + sem_post(response_sem); +#else sem_post(&response_sem); +#endif } static void process_high_priority(unsigned char *buffer) { @@ -1424,7 +1560,10 @@ static void process_mic_data(int bytes) { sequence=((mic_line_buffer[0]&0xFF)<<24)+((mic_line_buffer[1]&0xFF)<<16)+((mic_line_buffer[2]&0xFF)<<8)+(mic_line_buffer[3]&0xFF); b=4; for(i=0;ifreedv) { add_freedv_mic_sample(transmitter,sample); @@ -1445,9 +1584,15 @@ void new_protocol_process_local_mic(unsigned char *buffer,int le) { b=0; for(i=0;ifreedv) { @@ -1533,4 +1678,6 @@ fprintf(stderr,"new_protocol_timer_thread\n"); // } // } } + // DL1YCF: added return statement to make compiler happy. + return NULL; } diff --git a/new_protocol.h b/new_protocol.h index fa090678..659c7213 100644 --- a/new_protocol.h +++ b/new_protocol.h @@ -50,7 +50,11 @@ #define MIC_SAMPLES 64 extern int data_socket; +#ifdef __APPLE__ +extern sem_t *response_sem; +#else extern sem_t response_sem; +#endif /* extern long response_sequence; diff --git a/new_protocol_programmer.c b/new_protocol_programmer.c index ea4daf82..939ef04a 100644 --- a/new_protocol_programmer.c +++ b/new_protocol_programmer.c @@ -172,7 +172,11 @@ void *programmer_thread(void *arg) { // wait for the response to the erase command clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec+=120; // wait for 30 seconds +#ifdef __APPLE__ + result=sem_trywait(response_sem); +#else result=sem_timedwait(&response_sem,&ts); +#endif if(result==-1) { if(errno == ETIMEDOUT) { fprintf(stderr,"timedout waiting for response for erase (start)\n"); @@ -190,7 +194,11 @@ void *programmer_thread(void *arg) { // wait for the erase to complete clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec+=120; // wait for 30 seconds +#ifdef __APPLE__ + result=sem_trywait(response_sem); +#else result=sem_timedwait(&response_sem,&ts); +#endif if(result==-1) { if(errno == ETIMEDOUT) { fprintf(stderr,"timedout waiting for response for erase (complete)\n"); @@ -211,7 +219,11 @@ void *programmer_thread(void *arg) { programmer_send_block(b); clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec+=5; // wait for 5 seconds +#ifdef __APPLE__ + result=sem_trywait(response_sem); +#else result=sem_timedwait(&response_sem,&ts); +#endif if(result==-1) { if(errno == ETIMEDOUT) { fprintf(stderr,"timedout waiting for response for sent block\n"); @@ -227,5 +239,7 @@ void *programmer_thread(void *arg) { } block_sequence++; } - + + // DL1YCF added return statement to make compiler happy. + return NULL; } diff --git a/old_discovery.c b/old_discovery.c index 57ea09e3..e47c4656 100644 --- a/old_discovery.c +++ b/old_discovery.c @@ -133,7 +133,8 @@ static void discover(struct ifaddrs* iface) { //static void *discover_receive_thread(void* arg) { static gpointer discover_receive_thread(gpointer data) { struct sockaddr_in addr; - int len; + // DL1YCF changed int to socklen_t + socklen_t len; unsigned char buffer[2048]; int bytes_read; struct timeval tv; @@ -183,6 +184,7 @@ fprintf(stderr,"discover_receive_thread\n"); #else strcpy(discovered[devices].name,"Hermes Lite"); #endif + break; case DEVICE_ORION2: strcpy(discovered[devices].name,"Orion 2"); @@ -222,6 +224,8 @@ fprintf(stderr,"discover_receive_thread\n"); } fprintf(stderr,"discovery: exiting discover_receive_thread\n"); g_thread_exit(NULL); + // DL1YCF added return statement to make compiler happy. + return NULL; } void old_discovery() { diff --git a/old_protocol.c b/old_protocol.c index 3459bec2..8b2aa128 100644 --- a/old_protocol.c +++ b/old_protocol.c @@ -131,6 +131,9 @@ static double phase=0.0; static int running; static long ep4_sequence; +// DL1YCF added this variable for lost-package-check +static long last_seq_num=0; + static int current_rx=0; static int samples=0; @@ -161,7 +164,8 @@ static int command=1; static GThread *receive_thread_id; static void start_receive_thread(); static gpointer receive_thread(gpointer arg); -static void process_ozy_input_buffer(char *buffer); +// DL1YCF changed buffer to uchar* +static void process_ozy_input_buffer(unsigned char *buffer); static void process_bandscope_buffer(char *buffer); void ozy_send_buffer(); @@ -169,9 +173,11 @@ static unsigned char metis_buffer[1032]; static long send_sequence=-1; static int metis_offset=8; -static int metis_write(unsigned char ep,char* buffer,int length); +// DL1YCF changed buffer to uchar* +static int metis_write(unsigned char ep,unsigned char* buffer,int length); static void metis_start_stop(int command); -static void metis_send_buffer(char* buffer,int length); +// DL1YCF changed buffer to uchar* +static void metis_send_buffer(unsigned char* buffer,int length); static void metis_restart(); #define COMMON_MERCURY_FREQUENCY 0x80 @@ -374,7 +380,8 @@ static void start_receive_thread() { static gpointer receive_thread(gpointer arg) { struct sockaddr_in addr; - int length; + // DL1YCF changed from int to socklen_t + socklen_t length; unsigned char buffer[2048]; int bytes_read; int ep; @@ -414,6 +421,11 @@ static gpointer receive_thread(gpointer arg) { // get the sequence number sequence=((buffer[4]&0xFF)<<24)+((buffer[5]&0xFF)<<16)+((buffer[6]&0xFF)<<8)+(buffer[7]&0xFF); + // DL1YCF: added check on lost packets + if (sequence != last_seq_num+1) { + fprintf(stderr,"SEQ ERROR: last %ld, recvd %ld\n", last_seq_num, sequence); + } + last_seq_num=sequence; switch(ep) { case 6: // EP6 // process the data @@ -453,9 +465,12 @@ static gpointer receive_thread(gpointer arg) { break; } } + // DL1YCF added return statement to make compiler happy. + return NULL; } -static void process_ozy_input_buffer(char *buffer) { +// Dl1YCF changed buffer to uchar* +static void process_ozy_input_buffer(unsigned char *buffer) { int i,j; int r; int b=0; @@ -702,10 +717,18 @@ void old_protocol_process_local_mic(unsigned char *buffer,int le) { // always 48000 samples per second b=0; for(i=0;i<720;i++) { + // avoid pointer increments in logical-or constructs, as the sequence + // is undefined if(le) { - sample = (short)((buffer[b++]&0xFF) | (buffer[b++]<<8)); + // DL1YCF: changed this to two statements such that the order of pointer increments + // becomes clearly defined. + sample = (short) (buffer[b++]&0xFF); + sample |= (short) (buffer[b++]<<8); } else { - sample = (short)((buffer[b++]<<8) | (buffer[b++]&0xFF)); + // DL1YCF: changed this to two statements such that the order of pointer increments + // becomes clearly defined. + sample = (short)(buffer[b++]<<8); + sample |= (short) (buffer[b++]&0xFF); } #ifdef FREEDV if(active_receiver->freedv) { @@ -724,7 +747,10 @@ static void process_bandscope_buffer(char *buffer) { } */ - +#ifdef PROTOCOL_DEBUG +// DL1YCF Debug: save last values and log changes +static unsigned char last_c1[20], last_c2[20], last_c3[20], last_c4[20], last_mox; +#endif void ozy_send_buffer() { @@ -986,7 +1012,25 @@ void ozy_send_buffer() { { BAND *band=band_get_current_band(); int power=0; +#ifdef STEMLAB_FIX + // + // DL1YCF: + // On my HAMlab RedPitaya-based SDR transceiver, CW is generated on-board the RP. + // However, while in CW mode, DriveLevel changes do not become effective. + // If the CW paddle is hit, the new PTT state is sent to piHPSDR, then the TX drive + // is sent the next time "command 3" is performed, but this often is too late and + // CW is generated with zero DriveLevel. + // Therefore, when in CW mode, send the TX drive level also when receiving. + // + if(split) { + mode=vfo[1].mode; + } else { + mode=vfo[0].mode; + } + if(isTransmitting() || (mode == modeCWU) || (mode == modeCWL)) { +#else if(isTransmitting()) { +#endif if(tune && !transmitter->tune_use_drive) { power=(int)((double)transmitter->drive_level/100.0*(double)transmitter->tune_percent); } else { @@ -1166,6 +1210,37 @@ void ozy_send_buffer() { } } +#ifdef PROTOCOL_DEBUG +// +// DL1YCF debug: +// look for changed parameters and log them +// This is great for debugging protocol problems, +// such as the HAMlab CW error fixed above, so I +// leave it here deactivated +// + int ind = output_buffer[C0] >> 1; + if (last_c1[ind] != output_buffer[C1]) { + fprintf(stderr, "C0=%x Old C1=%x New C1=%x\n", 2*ind,last_c1[ind], output_buffer[C1]); + last_c1[ind]=output_buffer[C1]; + } + if (last_c2[ind] != output_buffer[C2]) { + fprintf(stderr, "C0=%x Old C2=%x New C2=%x\n", 2*ind,last_c2[ind], output_buffer[C2]); + last_c2[ind]=output_buffer[C2]; + } + if (last_c3[ind] != output_buffer[C3]) { + fprintf(stderr, "C0=%x Old C3=%x New C3=%x\n", 2*ind,last_c3[ind], output_buffer[C3]); + last_c3[ind]=output_buffer[C3]; + } + if (last_c4[ind] != output_buffer[C4]) { + fprintf(stderr, "C0=%x Old C4=%x New C4=%x\n", 2*ind,last_c4[ind], output_buffer[C4]); + last_c4[ind]=output_buffer[C4]; + } + if ((output_buffer[C0] & 1) != last_mox) { + fprintf(stderr, "Last Mox=%d New Mox=%d\n", last_mox, output_buffer[C0] & 1); + last_mox=output_buffer[C0] & 1; + } +#endif + #ifdef USBOZY // // if we have a USB interfaced Ozy device: @@ -1215,7 +1290,8 @@ static int ozyusb_write(char* buffer,int length) } #endif -static int metis_write(unsigned char ep,char* buffer,int length) { +// DL1YCF change buffer to uchar* +static int metis_write(unsigned char ep,unsigned char* buffer,int length) { int i; // copy the buffer over @@ -1248,11 +1324,33 @@ static int metis_write(unsigned char ep,char* buffer,int length) { static void metis_restart() { // reset metis frame - metis_offset==8; + // DL1YCF change == to = in the next line + metis_offset=8; // reset current rx current_rx=0; +#ifdef STEMLAB_FIX + // DL1YCF: + // My RedPitaya HPSDR "clone" won't start up + // if too many commands are sent here. Note these + // packets are only there for sync-ing in the clock + // source etc. + // Note that always two 512-byte OZY buffers are + // combined into one METIS packet. + // + command=1; // ship out a "C0=0" and a "set tx" command + ozy_send_buffer(); + ozy_send_buffer(); + command=2; // ship out a "C0=0" and a "set rx" command for RX1 + ozy_send_buffer(); + ozy_send_buffer(); + + // DL1YCF: reset for the next commands + current_rx=0; + command=1; +#else + // DL1YCF this is the original code, which does not do what it pretends .... // send commands twice command=1; do { @@ -1262,6 +1360,7 @@ static void metis_restart() { do { ozy_send_buffer(); } while (command!=1); +#endif sleep(1); @@ -1293,7 +1392,8 @@ static void metis_start_stop(int command) { #endif } -static void metis_send_buffer(char* buffer,int length) { +// DL1YCF changedbuffer to uchar * +static void metis_send_buffer(unsigned char* buffer,int length) { if(sendto(data_socket,buffer,length,0,(struct sockaddr*)&data_addr,data_addr_length)!=length) { perror("sendto socket failed for metis_send_data\n"); } diff --git a/portaudio.c b/portaudio.c new file mode 100644 index 00000000..69f77fc7 --- /dev/null +++ b/portaudio.c @@ -0,0 +1,396 @@ +#ifdef PORTAUDIO +// +// DL1YCF: if PortAudio is NOT used, this file is empty, and audio.c +// is used instead. +// Here we also implement two (hopefully useful) functions: +// - a dummy two-tone 'Microphone' device +// + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "new_protocol.h" +#include "old_protocol.h" +#ifdef RADIOBERRY +#include "radioberry.h" +#endif +#include "radio.h" +#include "receiver.h" +#include "portaudio.h" +#include "math.h" // for sintab, two-tone generator + +static PaStream *record_handle=NULL; + +#define MAXDEVICES 12 + +const char *input_devices[MAXDEVICES]; +const char *output_devices[MAXDEVICES]; + +int n_input_devices=0; +int n_output_devices=0; + +static int in_device_no[MAXDEVICES]; +static int out_device_no[MAXDEVICES]; + +static unsigned char *mic_buffer=NULL; +static int mic_buffer_size; +static int audio_buffer_size=256; + +// +// Dummy Two-tone input device +// +static int TwoTone=0; +#define lentab 480 +static float sintab[lentab]; +static int tonept; +#define twopi 6.2831853071795864769252867665590 +#define factab (twopi/480) + + +// +// AUDIO_GET_CARDS +// +// This inits PortAudio and looks for suitable input and output channels +// +void audio_get_cards() +{ + int i, numDevices; + const PaDeviceInfo *deviceInfo; + PaStreamParameters inputParameters, outputParameters; + + PaError err; + + // generate sine tab + for (i=0; i< lentab; i++) sintab[i] = 0.35*(sin(7*i*factab)+sin(19*i*factab)); + + err = Pa_Initialize(); + if( err != paNoError ) + { + fprintf(stderr, "PORTAUDIO ERROR: Pa_Initialize: %s\n", Pa_GetErrorText(err)); + return; + } + numDevices = Pa_GetDeviceCount(); + if( numDevices < 0 ) return; + + n_input_devices=0; + n_output_devices=0; + + for( i=0; iname; + in_device_no[n_input_devices++] =i; + } + fprintf(stderr,"PORTAUDIO INPUT DEVICE, No=%d, Name=%s\n", i, deviceInfo->name); + } + + outputParameters.device = i; + outputParameters.channelCount = 1; + outputParameters.sampleFormat = paFloat32; + outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ + outputParameters.hostApiSpecificStreamInfo = NULL; + if (Pa_IsFormatSupported(NULL, &outputParameters, 48000.0) == paFormatIsSupported) { + if (n_output_devices < MAXDEVICES) { + output_devices[n_output_devices]=deviceInfo->name; + out_device_no[n_output_devices++] =i; + } + fprintf(stderr,"PORTAUDIO OUTPUT DEVICE, No=%d, Name=%s\n", i, deviceInfo->name); + } + } +} + +// +// AUDIO_OPEN_INPUT +// +// open a PA stream that connects to the TX microphone +// The PA callback function then sends the data to the transmitter +// + +int pa_mic_cb(const void*, void*, unsigned long, const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void*); +unsigned char *micbuffer = NULL; + +int audio_open_input() +{ + PaError err; + PaStreamParameters inputParameters, outputParameters; + long framesPerBuffer; + int i; + + fprintf(stderr,"audio_open_input: %d\n",transmitter->input_device); + if(transmitter->input_device<0 || transmitter->input_device>=n_input_devices) { + transmitter->input_device=0; + return -1; + } + + switch(protocol) { + case ORIGINAL_PROTOCOL: + framesPerBuffer = 720; + break; + case NEW_PROTOCOL: + framesPerBuffer = 64; + break; +#ifdef RADIOBERRY + case RADIOBERRY_PROTOCOL: + framesPerBuffer = 1024; + break; +#endif + default: + break; + } + + bzero( &inputParameters, sizeof( inputParameters ) ); //not necessary if you are filling in all the fields + inputParameters.channelCount = 1; + inputParameters.device = in_device_no[transmitter->input_device]; + inputParameters.hostApiSpecificStreamInfo = NULL; + inputParameters.sampleFormat = paFloat32; + inputParameters.suggestedLatency = Pa_GetDeviceInfo(in_device_no[transmitter->input_device])->defaultLowInputLatency ; + inputParameters.hostApiSpecificStreamInfo = NULL; //See you specific host's API docs for info on using this field + + err = Pa_OpenStream(&record_handle, &inputParameters, NULL, 48000.0, framesPerBuffer, paNoFlag, pa_mic_cb, NULL); + if (err != paNoError) { + fprintf(stderr, "PORTAUDIO ERROR: AOI open stream: %s\n",Pa_GetErrorText(err)); + return -1; + } + + err = Pa_StartStream(record_handle); + if (err != paNoError) { + fprintf(stderr, "PORTAUDIO ERROR: AOI start stream:%s\n",Pa_GetErrorText(err)); + return -1; + } + mic_buffer=(unsigned char *)malloc(2*framesPerBuffer); + mic_buffer_size=framesPerBuffer; + TwoTone=0; + if (transmitter->input_device == 0) { + tonept=0; + TwoTone=1; + } + return 0; +} + +// +// PortAudio call-back function for Audio input +// +int pa_mic_cb(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userdata) +{ + const float *in = (float *)inputBuffer; + int i; + short isample; + unsigned char *p; + +// +// Convert input buffer in paFloat32 into a sequence of 16-bit +// values in the mic buffer +// + if (mic_buffer == NULL) return paAbort; + + p=mic_buffer; + if (in == NULL || framesPerBuffer != mic_buffer_size) { + for (i=0; i> 8)& 0xFF; + } + } else { + for (i=0; i> 8)& 0xFF; + } + } +// +// Call routine to send mic buffer +// + switch(protocol) { + case ORIGINAL_PROTOCOL: + old_protocol_process_local_mic(mic_buffer,1); + break; + case NEW_PROTOCOL: + new_protocol_process_local_mic(mic_buffer,1); + break; +#ifdef RADIOBERRY + case RADIOBERRY_PROTOCOL: + radioberry_protocol_process_local_mic(mic_buffer,1); + break; +#endif + default: + break; + } + return paContinue; +} + +// +// AUDIO_OPEN_OUTPUT +// +// open a PA stream for data from one of the RX +// +int audio_open_output(RECEIVER *rx) +{ + PaError err; + PaStreamParameters outputParameters; + long framesPerBuffer=(long) audio_buffer_size; + + int padev = out_device_no[rx->audio_device]; + fprintf(stderr,"audio_open_output: %d PADEV=%d\n",rx->audio_device,padev); + if(rx->audio_device<0 || rx->audio_device>=n_output_devices) { + rx->audio_device=-1; + return -1; + } + bzero( &outputParameters, sizeof( outputParameters ) ); //not necessary if you are filling in all the fields + outputParameters.channelCount = 1; // Always MONO + outputParameters.device = padev; + outputParameters.hostApiSpecificStreamInfo = NULL; + outputParameters.sampleFormat = paFloat32; + outputParameters.suggestedLatency = Pa_GetDeviceInfo(padev)->defaultLowOutputLatency ; + outputParameters.hostApiSpecificStreamInfo = NULL; //See you specific host's API docs for info on using this field + + // Try using AudioWrite without a call-back function + + rx->playback_buffer=malloc(audio_buffer_size*sizeof(float)); + rx->playback_offset=0; + err = Pa_OpenStream(&(rx->playback_handle), NULL, &outputParameters, 48000.0, framesPerBuffer, paNoFlag, NULL, NULL); + if (err != paNoError) { + fprintf(stderr,"PORTAUDIO ERROR: AOO open stream: %s\n",Pa_GetErrorText(err)); + rx->playback_handle = NULL; + if (rx->playback_buffer) free(rx->playback_buffer); + rx->playback_buffer = NULL; + return -1; + } + + err = Pa_StartStream(rx->playback_handle); + if (err != paNoError) { + fprintf(stderr,"PORTAUDIO ERROR: AOO start stream:%s\n",Pa_GetErrorText(err)); + rx->playback_handle=NULL; + if (rx->playback_buffer) free(rx->playback_buffer); + rx->playback_buffer = NULL; + return -1; + } + // Write one buffer to avoid under-flow errors + // (this gives us 5 msec to pass before we have to call audio_write the first time) + bzero(rx->playback_buffer, (size_t) audio_buffer_size*sizeof(float)); + err=Pa_WriteStream(rx->playback_handle, (void *) rx->playback_buffer, (unsigned long) audio_buffer_size); + return 0; +} + +// +// AUDIO_CLOSE_INPUT +// +// close a TX microphone stream +// +void audio_close_input() +{ + PaError err; + + fprintf(stderr,"AudioCloseInput: %d\n", transmitter->input_device); + + if(record_handle!=NULL) { + err = Pa_StopStream(record_handle); + if (err != paNoError) { + fprintf(stderr,"PORTAUDIO ERROR: ACI stop stream: %s\n",Pa_GetErrorText(err)); + } + err = Pa_CloseStream(record_handle); + if (err != paNoError) { + fprintf(stderr,"PORTAUDIO ERROR: ACI close stream: %s\n",Pa_GetErrorText(err)); + } + record_handle=NULL; + } + if(mic_buffer!=NULL) { + free(mic_buffer); + mic_buffer=NULL; + } +} + +// +// AUDIO_CLOSE_OUTPUT +// +// shut down the stream connected with audio from one of the RX +// +void audio_close_output(RECEIVER *rx) { + PaError err; + + fprintf(stderr,"AudioCloseOutput: %d\n", rx->audio_device); + +// free the buffer first, this then indicates to audio_write to do nothing + if(rx->playback_buffer!=NULL) { + free(rx->playback_buffer); + rx->playback_buffer=NULL; + } + + if(rx->playback_handle!=NULL) { + err = Pa_StopStream(rx->playback_handle); + if (err != paNoError) { + fprintf(stderr,"PORTAUDIO ERROR: ACO stop stream: %s\n",Pa_GetErrorText(err)); + } + err = Pa_CloseStream(rx->playback_handle); + if (err != paNoError) { + fprintf(stderr,"PORTAUDIO ERROR: ACO close stream: %s\n",Pa_GetErrorText(err)); + } + rx->playback_handle=NULL; + } +} + +// +// AUDIO_WRITE +// +// send RX audio data to a PA output stream +// we have to store the data such that the PA callback function +// can access it. +// +static int apt=0; +int audio_write (RECEIVER *rx, short r, short l) +{ + PaError err; + + if (rx->playback_handle != NULL && rx->playback_buffer != NULL) { + rx->playback_buffer[rx->playback_offset++] = (r + l) *0.000015259; // 65536 --> 1.0 + if (rx->playback_offset == audio_buffer_size) { + rx->playback_offset=0; + err=Pa_WriteStream(rx->playback_handle, (void *) rx->playback_buffer, (unsigned long) audio_buffer_size); + //if (err != paNoError) { + // fprintf(stderr,"PORTAUDIO ERROR: write stream dev=%d: %s\n",out_device_no[rx->audio_device],Pa_GetErrorText(err)); + // return -1; + // } + } + } + return 0; +} + +// +// CW audio write +// This is a dummy here because I think it is not correctly implemented in audio.c +// +void cw_audio_write(double sample) { +} + +#endif diff --git a/radio.c b/radio.c index 658c8f54..512687b2 100644 --- a/radio.c +++ b/radio.c @@ -121,7 +121,11 @@ static gint save_timer_id; DISCOVERED *radio=NULL; char property_path[128]; +#ifdef __APPLE__ +sem_t *property_sem; +#else sem_t property_sem; +#endif RECEIVER *receiver[MAX_RECEIVERS]; RECEIVER *active_receiver; @@ -317,7 +321,8 @@ void reconfigure_radio() { } else { gtk_fixed_move(GTK_FIXED(fixed),sliders,0,y); } - gtk_widget_show_all(sliders); + gtk_widget_show_all(sliders); // DL1YCF this shows both C25 and Alex ATT/Preamp sliders + att_type_changed(); // DL1YCF added here to hide the „wrong“ ones. } else { if(sliders!=NULL) { gtk_container_remove(GTK_CONTAINER(fixed),sliders); @@ -352,12 +357,21 @@ void start_radio() { gdk_window_set_cursor(gtk_widget_get_window(top_window),gdk_cursor_new(GDK_WATCH)); int rc; +#ifdef __APPLE__ + property_sem=sem_open("PROPERTY", O_CREAT, 0700, 0); + rc=(property_sem == SEM_FAILED); +#else rc=sem_init(&property_sem, 0, 0); +#endif if(rc!=0) { fprintf(stderr,"start_radio: sem_init failed for property_sem: %d\n", rc); exit(-1); } +#ifdef __APPLE__ + sem_post(property_sem); +#else sem_post(&property_sem); +#endif char text[256]; //for(i=0;ihz_per_pixel)); last_x=x; diff --git a/receiver.h b/receiver.h index 3bd59c4d..39bf084e 100644 --- a/receiver.h +++ b/receiver.h @@ -20,7 +20,11 @@ #define _RECEIVER_H #include +#ifdef PORTAUDIO +#include "portaudio.h" +#else #include +#endif #define AUDIO_BUFFER_SIZE 260 @@ -106,9 +110,14 @@ typedef struct _receiver { int local_audio; int mute_when_not_active; int audio_device; +#ifdef PORTAUDIO + PaStream *playback_handle; + float *playback_buffer; +#else snd_pcm_t *playback_handle; - int playback_offset; unsigned char *playback_buffer; +#endif + int playback_offset; int low_latency; int squelch_enable; diff --git a/rigctl.c b/rigctl.c index 6216fb3d..c1213928 100644 --- a/rigctl.c +++ b/rigctl.c @@ -82,8 +82,9 @@ int connect_cnt = 0; int rigctlGetFilterLow(); int rigctlGetFilterHigh(); -int rigctlSetFilterLow(int val); -int rigctlSetFilterHigh(int val); +// DL1YCF changed next to function to void +void rigctlSetFilterLow(int val); +void rigctlSetFilterHigh(int val); int new_level; int active_transmitter = 0; int rigctl_busy = 0; // Used to tell rigctl_menu that launch has already occured @@ -122,7 +123,8 @@ static int rigctl_timer = 0; typedef struct _client { int socket; - int address_length; + // Dl1YCF change from int to socklen_t + socklen_t address_length; struct sockaddr_in address; GThread *thread_id; } CLIENT; @@ -197,6 +199,8 @@ static gpointer set_rigctl_timer (gpointer data) { // Wait throttle time usleep(RIGCTL_TIMER_DELAY); rigctl_timer = 0; + // DL1YCF added return statement to make compiler happy. + return NULL; } // @@ -1930,16 +1934,16 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { if(agc_resp == 0) { active_receiver->agc = AGC_OFF; - } else if(agc_resp >0 && agc_resp <= 5 || (agc_resp == 84)) { + } else if((agc_resp >0 && agc_resp <= 5) || (agc_resp == 84)) { // DL1YCF: added () to improve readability active_receiver->agc = AGC_FAST; // fprintf(stderr,"GT command FAST\n"); - } else if(agc_resp >6 && agc_resp <= 10 || (agc_resp == 2*84)) { + } else if((agc_resp >6 && agc_resp <= 10) || (agc_resp == 2*84)) { // DL1YCF: added () to improve readability active_receiver->agc = AGC_MEDIUM; // fprintf(stderr,"GT command MED\n"); - } else if(agc_resp >11 && agc_resp <= 15 || (agc_resp == 3*84)) { + } else if((agc_resp >11 && agc_resp <= 15) || (agc_resp == 3*84)) { // DL1YCF: added () to improve readability active_receiver->agc = AGC_SLOW; //fprintf(stderr,"GT command SLOW\n"); - } else if(agc_resp >16 && agc_resp <= 20 || (agc_resp == 4*84)) { + } else if((agc_resp >16 && agc_resp <= 20) || (agc_resp == 4*84)) { // DL1YCF: added () to improve readability active_receiver->agc = AGC_LONG; // fprintf(stderr,"GT command LONG\n"); } @@ -2019,8 +2023,8 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { send_resp(client_sock,"IS00000;"); } } - else if((strcmp(cmd_str,"KS")==0) && (zzid_flag == 0) || - (strcmp(cmd_str,"CS")==0) && (zzid_flag==1)) { + else if(((strcmp(cmd_str,"KS")==0) && (zzid_flag == 0)) || // Dl1YCF added () to improve readablity + ((strcmp(cmd_str,"CS")==0) && (zzid_flag==1))) { // Dl1YCF added () to improve readablity // TS-2000 - KS - Set/Reads keying speed 0-060 max // PiHPSDR - ZZCS - Sets/Reads Keying speed if(len <=2) { @@ -3053,8 +3057,8 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { send_resp(client_sock,"?;"); } } - else if((strcmp(cmd_str,"SD")==0) && (zzid_flag == 0) || - (strcmp(cmd_str,"CD")==0) && (zzid_flag ==1)) { + else if(((strcmp(cmd_str,"SD")==0) && (zzid_flag == 0)) || // Dl1YCF added () to improve readablity + ((strcmp(cmd_str,"CD")==0) && (zzid_flag ==1))) { // Dl1YCF added () to improve readablity // PiHPSDR - ZZCD - Set/Read CW Keyer Hang Time // TS-2000 - SD - Set/Read Break In Delay // @@ -3123,7 +3127,8 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { // Determine how high above 127 we are..making a range of 114 from S0 to S9+60db // 5 is a fugdge factor that shouldn't be there - but seems to get us to S9=SM015 - level = abs(127+(level + (double) adc_attenuation[receiver[r]->adc]))+5; + // DL1YCF replaced abs by fabs, and changed 127 to floating point constant + level = fabs(127.0+(level + (double) adc_attenuation[receiver[r]->adc]))+5; // Clip the value just in case if(cmd_input[2] == '0') { @@ -3839,10 +3844,10 @@ int rigctlGetMode() { } } - -int rigctlSetFilterLow(int val){ +// Changed these two functions to void +void rigctlSetFilterLow(int val){ }; -int rigctlSetFilterHigh(int val){ +void rigctlSetFilterHigh(int val){ }; void set_freqB(long long new_freqB) { diff --git a/rx_menu.c b/rx_menu.c index 704de480..3e83bb16 100644 --- a/rx_menu.c +++ b/rx_menu.c @@ -113,12 +113,19 @@ static void mute_radio_cb(GtkWidget *widget, gpointer data) { active_receiver->mute_radio=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); } +// +// DL1YCF: +// possible the device has been changed: +// call audo_close_output with old device, audio_open_output with new one +// static void local_output_changed_cb(GtkWidget *widget, gpointer data) { - active_receiver->audio_device=(int)(long)data; -fprintf(stderr,"local_output_changed rx=%d to %d\n",active_receiver->id,active_receiver->audio_device); +//active_receiver->audio_device=(int)(long)data; + int newdev = (int)(long)data; +fprintf(stderr,"local_output_changed rx=%d from %d to %d\n",active_receiver->id,active_receiver->audio_device,newdev); if(active_receiver->local_audio) { - audio_close_output(active_receiver); - if(audio_open_output(active_receiver)==0) { + audio_close_output(active_receiver); // audio_close with OLD device + active_receiver->audio_device=newdev; // update rx to NEW device + if(audio_open_output(active_receiver)==0) { // audio_open with NEW device active_receiver->local_audio=1; } else { active_receiver->local_audio=0; diff --git a/stemlab_discovery.c b/stemlab_discovery.c index 19278846..e3ceb7d5 100644 --- a/stemlab_discovery.c +++ b/stemlab_discovery.c @@ -17,6 +17,208 @@ * */ +#ifdef __APPLE__ + + +// +// MacOS has no vahi, but it does have libcurl. +// Therefore we try to start the SDR app on the RedPitaya +// assuming is has the (fixed) ip address which can be +// read from $HOME/.rp.inet, if this does not succeed it +// defaults to 192.168.1.3. +// +// So, on MacOS, just configure your STEMLAB/HAMLAB to this +// fixed IP address and you need not open a browser to start +// SDR *before* you can use piHPSDR. +// +// Sure it's not perfect, but it makes life much easier for me. +// +#include +#include +#include +#include +#include +#include + +extern void status_text(const char *); + +static const char *appid = NULL; + +// +// Extract the list of apps from the JSON answer +// +static size_t app_list_callback(void *buffer, size_t size, size_t nmemb, void *data) { + const gchar *needle; + + needle="\"sdr_receiver_hpsdr\""; + if (g_strstr_len(buffer, size*nmemb, needle) != NULL) { + appid="sdr_receiver_hpsdr"; + } + + needle="\"sdr_transceiver_hpsdr\""; + if (g_strstr_len(buffer, size*nmemb, needle) != NULL) { + appid="sdr_transceiver_hpsdr"; + } + + needle="\"stemlab_sdr_transceiver_hpsdr\""; + if (g_strstr_len(buffer, size*nmemb, needle) != NULL) { + appid="stemlab_sdr_transceiver_hpsdr"; + } + + needle="\"hamlab_sdr_transceiver_hpsdr\""; + if (g_strstr_len(buffer, size*nmemb, needle) != NULL) { + appid="hamlab_sdr_transceiver_hpsdr"; + } + + if (appid) fprintf(stderr,"RedPitay WEB application to start: %s\n", appid); + return size * nmemb; +} + +void stemlab_discovery() { + // this one is used "as the last resort", if nothing else is found. + size_t len; + char inet[20]; + char txt[150]; + CURL *curl_handle; + CURLcode curl_error; + FILE *fpin; + char *p; + + fprintf(stderr,"Stripped-down STEMLAB/HAMLAB discovery...\n"); +// +// Try to read inet addr from $HOME/.rp.inet, otherwise take 192.168.1.3 +// + strcpy(inet,"192,168.1.3"); + p=getenv("HOME"); + if (p) { + strncpy(txt,p, (size_t) 100); // way less than size of txt + } else { + strcpy(txt,"."); + } + strcat(txt,"/.rp.inet"); + fprintf(stderr,"Trying to read inet addr from file=%s\n", txt); + fpin=fopen(txt, "r"); + if (fpin) { + len=100; + p=txt; + len=getline(&p, &len, fpin); + // not txt now contains the trailing newline character + while (*p != 0) { + if (*p == '\n') *p = 0; + p++; + } + if (len < 20) strcpy(inet,txt); + } + fclose(fpin); + fprintf(stderr,"STEMLAB: using inet addr %s\n", inet); +// +// Do a HEAD request (poor curl's ping) to see whether the device is on-line +// allow a 15 sec time-out + status_text("Looking for a STEMLAB web server ..."); + curl_handle = curl_easy_init(); + if (curl_handle == NULL) { + fprintf(stderr, "stemlab_start: Failed to create cURL handle\n"); + return; + } + sprintf(txt,"http://%s",inet); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_URL, txt); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_NOBODY, (long) 1); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, (long) 15); + curl_error = curl_easy_perform(curl_handle); + curl_easy_cleanup(curl_handle); + if (curl_error == CURLE_OPERATION_TIMEDOUT) { + sprintf(txt,"No response from web server at %s", inet); + status_text(txt); + fprintf(stderr,"%s\n",txt); + } + if (curl_error != CURLE_OK) { + fprintf(stderr, "STEMLAB ping error: %s\n", curl_easy_strerror(curl_error)); + return; + } + +// +//obtain a list of apps, and choose the right one by looking for the following +//target strings (in that order). Whatever is found first, is started. Then, we rely +//on the original discovery() to discover the device. +// +//hamlab_sdr_transceiver_hpsdr +//stemlab_sdr_transceiver_hpsdr +//sdr_transceiver_hpsdr +//sdr_receiver_hpsdr +// + curl_handle = curl_easy_init(); + if (curl_handle == NULL) { + fprintf(stderr, "stemlab_start: Failed to create cURL handle\n"); + return; + } + sprintf(txt,"http://%s/bazaar?apps=", inet); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_URL, txt); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, (long) 60); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, app_list_callback); + curl_error = curl_easy_perform(curl_handle); + curl_easy_cleanup(curl_handle); + if (curl_error == CURLE_OPERATION_TIMEDOUT) { + status_text("No Response from RedPitaya in 60 secs"); + fprintf(stderr,"60-sec TimeOut met when trying to get list of HPSDR apps from RedPitaya\n"); + } + if (curl_error != CURLE_OK) { + fprintf(stderr, "STEMLAB app-list error: %s\n", curl_easy_strerror(curl_error)); + return; + } + +// +// Now we actually start the hpsdr application +// Actually, try to stop it first, then re-start it. +// + if (appid) { + curl_handle = curl_easy_init(); + if (curl_handle == NULL) { + fprintf(stderr, "stemlab_start: Failed to create cURL handle\n"); + return; + } + sprintf(txt,"http://%s/bazaar?stop=%s",inet,appid); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_URL, txt); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, (long) 60); + curl_error = curl_easy_perform(curl_handle); + if (curl_error == CURLE_OPERATION_TIMEDOUT) { + fprintf(stderr,"60-sec TimeOut met when trying to stop HPSDR app on RedPitaya\n"); + } + if (curl_error != CURLE_OK) { + fprintf(stderr, "STEMLAB app-start error: %s\n", curl_easy_strerror(curl_error)); + } + curl_handle = curl_easy_init(); + if (curl_handle == NULL) { + fprintf(stderr, "stemlab_start: Failed to create cURL handle\n"); + return; + } + sprintf(txt,"http://%s/bazaar?start=%s",inet,appid); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_URL, txt); + curl_error = curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, (long) 60); + curl_error = curl_easy_perform(curl_handle); + if (curl_error == CURLE_OPERATION_TIMEDOUT) { + fprintf(stderr,"60-sec TimeOut met when trying to start HPSDR app on RedPitaya\n"); + } + if (curl_error != CURLE_OK) { + fprintf(stderr, "STEMLAB app-start error: %s\n", curl_easy_strerror(curl_error)); + } + + } + // Whether or net we have successfully started the HPSDR application on the RedPitaya, + // we now return to the regular HPSDR protocol handling code that will eventually detect + // the "board". If this code does not work, you have to open a browser and start the HPSDR + // application manually. +} + +// dummy function +void stemlab_cleanup() { +} + +// dummy function, never called +void stemlab_start_app() { +} + +#else + #include #include #include @@ -324,3 +526,4 @@ void stemlab_cleanup(void) { curl_global_cleanup(); } } +#endif diff --git a/step_menu.c b/step_menu.c index d1d0a873..233a5b08 100644 --- a/step_menu.c +++ b/step_menu.c @@ -52,6 +52,10 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d static gboolean step_select_cb (GtkWidget *widget, gpointer data) { step=steps[(uintptr_t)data]; vfo_update(); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } void step_menu(GtkWidget *parent) { diff --git a/store_menu.c b/store_menu.c index 80f38718..2bb38a91 100644 --- a/store_menu.c +++ b/store_menu.c @@ -83,6 +83,10 @@ static gboolean store_select_cb (GtkWidget *widget, gpointer data) { // Save in the file now.. memSaveState(); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } static gboolean recall_select_cb (GtkWidget *widget, gpointer data) { @@ -107,6 +111,10 @@ static gboolean recall_select_cb (GtkWidget *widget, gpointer data) { vfo_mode_changed(mem[index].mode); g_idle_add(ext_vfo_update,NULL); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } void store_menu(GtkWidget *parent) { diff --git a/transmitter.c b/transmitter.c index d2c12ec4..aeac2509 100644 --- a/transmitter.c +++ b/transmitter.c @@ -63,6 +63,8 @@ static int waterfall_resample=8; int key = 0; +// DL1YCF added next line. +extern void cw_audio_write(double sample); static gint update_out_of_band(gpointer data) { TRANSMITTER *tx=(TRANSMITTER *)data; tx->out_of_band=0; diff --git a/tx_menu.c b/tx_menu.c index a746fe18..7e08fc99 100644 --- a/tx_menu.c +++ b/tx_menu.c @@ -160,6 +160,10 @@ static void local_input_changed_cb(GtkWidget *widget, gpointer data) { static gboolean emp_cb (GtkWidget *widget, gpointer data) { pre_emphasize=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); tx_set_pre_emphasize(transmitter,pre_emphasize); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } static void tune_value_changed_cb(GtkWidget *widget, gpointer data) { diff --git a/tx_panadapter.c b/tx_panadapter.c index eaf6e8fa..43f6e5cc 100644 --- a/tx_panadapter.c +++ b/tx_panadapter.c @@ -144,7 +144,8 @@ tx_panadapter_motion_notify_event_cb (GtkWidget *widget, &x, &y, &state); - if((state & GDK_BUTTON1_MASK == GDK_BUTTON1_MASK) || pressed) { + // DL1YCF: added a pair of () to fix an error + if(((state & GDK_BUTTON1_MASK) == GDK_BUTTON1_MASK) || pressed) { int moved=last_x-x; vfo_move((long long)((float)moved*hz_per_pixel)); last_x=x; @@ -164,6 +165,10 @@ tx_panadapter_scroll_event_cb (GtkWidget *widget, } else { vfo_move(-step); } + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } void tx_panadapter_update(TRANSMITTER *tx) { diff --git a/vfo.c b/vfo.c index 61262d86..a279a3a4 100644 --- a/vfo.c +++ b/vfo.c @@ -513,6 +513,10 @@ vfo_scroll_event_cb (GtkWidget *widget, } else { vfo_move(-step); } + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } diff --git a/vfo_menu.c b/vfo_menu.c index e2356ea4..e47936d2 100644 --- a/vfo_menu.c +++ b/vfo_menu.c @@ -161,6 +161,10 @@ static gboolean freqent_select_cb (GtkWidget *widget, gpointer data) { } } vfo_update(); + // DL1YCF added return statement to make the compiler happy. + // however I am unsure about the correct return value. + // I would have coded this as a void function. + return FALSE; } static void rit_cb(GtkComboBox *widget,gpointer data) { diff --git a/vox_menu.c b/vox_menu.c index 460ac435..e14d5f2c 100644 --- a/vox_menu.c +++ b/vox_menu.c @@ -91,6 +91,8 @@ static gpointer level_thread(gpointer arg) { g_idle_add(level_update,NULL); usleep(100000); // 100ms } + // DL1YCF added return statement to make compilers happy. + return NULL; } static void cleanup() { diff --git a/waterfall.c b/waterfall.c index b673f802..bccebcc4 100644 --- a/waterfall.c +++ b/waterfall.c @@ -63,7 +63,8 @@ waterfall_configure_event_cb (GtkWidget *widget, display_height=gtk_widget_get_allocated_height (widget); rx->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, display_width, display_height); - char *pixels = gdk_pixbuf_get_pixels (rx->pixbuf); + // DL1YCF changed to uchar + unsigned char *pixels = gdk_pixbuf_get_pixels (rx->pixbuf); memset(pixels, 0, display_width*display_height*3); @@ -122,7 +123,8 @@ void waterfall_update(RECEIVER *rx) { float *samples; if(rx->pixbuf) { - char *pixels = gdk_pixbuf_get_pixels (rx->pixbuf); + // DL1YCF changed to uchar + unsigned char *pixels = gdk_pixbuf_get_pixels (rx->pixbuf); int width=gdk_pixbuf_get_width(rx->pixbuf); int height=gdk_pixbuf_get_height(rx->pixbuf); @@ -169,7 +171,8 @@ void waterfall_update(RECEIVER *rx) { float sample; int average=0; - char *p; + // DL1YCF changed to uchar + unsigned char *p; p=pixels; samples=rx->pixel_samples; for(i=0;i