From: c vw Date: Fri, 8 Jun 2018 10:13:55 +0000 (+0200) Subject: Port to MacOS, Compiler warnings removed, thus found many errors. X-Git-Url: https://git.rkrishnan.org/%5B/simplejson?a=commitdiff_plain;h=0a3b736fa514384eb6c05c67c17368993ac39b3b;p=pihpsdr.git Port to MacOS, Compiler warnings removed, thus found many errors. Includes two fixes for STEMlab/HAMlab. --- diff --git a/MacOS/Info.plist b/MacOS/Info.plist new file mode 100644 index 0000000..9bca968 --- /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 0000000..610abe0 --- /dev/null +++ b/MacOS/PkgInfo @@ -0,0 +1 @@ +APPLpihp diff --git a/MacOS/hpsdr.icns b/MacOS/hpsdr.icns new file mode 100644 index 0000000..4d1f6a8 Binary files /dev/null and b/MacOS/hpsdr.icns differ diff --git a/MacOS/hpsdr.png b/MacOS/hpsdr.png new file mode 100755 index 0000000..ea96fd7 Binary files /dev/null and b/MacOS/hpsdr.png differ diff --git a/MacOS/pihpsdr.sh b/MacOS/pihpsdr.sh new file mode 100755 index 0000000..314c755 --- /dev/null +++ b/MacOS/pihpsdr.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# +# Launch the exe file from a shell. +# This is the only way to make a GTK app bundle +# It still relies on a working GTK installation +# +# A full-fledged wrapper here would set dozens of +# environment variables. +# +this=`dirname $0` +cd $this/../Resources + +exec $this/pihpsdr-bin diff --git a/Makefile b/Makefile index a27c99e..0b89ea6 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,15 @@ GIT_DATE := $(firstword $(shell git --no-pager show --date=short --format="%ai" --name-only)) GIT_VERSION := $(shell git describe --abbrev=0 --tags) +#DL1YCF: this uses PortAudio rather than ALSA +PORTAUDIO_INCLUDE=PORTAUDIO + +#DL1YCF: un-commment this line for activate work-around some RedPitaty HPSDR bugs +STEMLAB_FIX_OPTION=-DSTEMLAB_FIX + +#DL1YCF: un-commment if you need librt (probably always, except on MacOS) +#LIBRT= -lrt + # uncomment the line below to include GPIO #GPIO_INCLUDE=GPIO @@ -27,18 +36,31 @@ GIT_VERSION := $(shell git describe --abbrev=0 --tags) # uncomment the line to below include support local CW keyer #LOCALCW_INCLUDE=LOCALCW -# uncomment the line below to include support for STEMlab discovery +# uncomment the line below to include support for STEMlab discovery (does not work on MacOS) #STEMLAB_DISCOVERY=STEMLAB_DISCOVERY +# uncomment the line below to include support for stripped-down STEMlab discovery that works on MacOS +STEMLAB_DISCOVERY=STEMLAB_DISCOVERY_MAC + #uncomment the line below for the platform being compiled on -UNAME_N=raspberrypi +UNAME_N=MacOS +#UNAME_N=raspberrypi #UNAME_N=odroid #UNAME_N=up #UNAME_N=pine64 #UNAME_N=jetsen CC=gcc + +ifeq ($(UNAME_N),MacOS) +# +# This is only necessary for "make app", since the "patched" +# library names are longer +# +LINK=gcc -headerpad_max_install_names +else LINK=gcc +endif # uncomment the line below for various debug facilities #DEBUG_OPTION=-D DEBUG @@ -164,6 +186,17 @@ ifeq ($(I2C_INCLUDE),I2C) I2C_OBJS=i2c.o endif +# +# STEMLAB_DISCOVERY_MAC depends on curl but not on avahi +# +ifeq ($(STEMLAB_DISCOVERY), STEMLAB_DISCOVERY_MAC) +STEMLAB_OPTIONS=-D STEMLAB_DISCOVERY `pkg-config --cflags libcurl` +STEMLAB_LIBS=`pkg-config --libs libcurl` +STEMLAB_SOURCES=stemlab_discovery.c +STEMLAB_HEADERS=stemlab_discovery.h +STEMLAB_OBJS=stemlab_discovery.o +endif + ifeq ($(STEMLAB_DISCOVERY), STEMLAB_DISCOVERY) STEMLAB_OPTIONS=-D STEMLAB_DISCOVERY \ `pkg-config --cflags avahi-gobject` \ @@ -177,12 +210,23 @@ endif GTKINCLUDES=`pkg-config --cflags gtk+-3.0` GTKLIBS=`pkg-config --libs gtk+-3.0` +ifeq ($(PORTAUDIO_INCLUDE), PORTAUDIO) +PORTAUDIO_OPTIONS=-DPORTAUDIO +AUDIO_LIBS=-lportaudio +else AUDIO_LIBS=-lasound #AUDIO_LIBS=-lsoundio +endif + +OPTIONS=-g -Wno-deprecated-declarations $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(RADIOBERRY_OPTIONS) \ + $(USBOZY_OPTIONS) $(I2C_OPTIONS) $(GPIO_OPTIONS) $(LIMESDR_OPTIONS) $(FREEDV_OPTIONS) \ + $(LOCALCW_OPTIONS) $(PSK_OPTIONS) $(STEMLAB_OPTIONS) $(STEMLAB_FIX_OPTION) \ + $(PORTAUDIO_OPTIONS) \ + -D GIT_DATE='"$(GIT_DATE)"' -D GIT_VERSION='"$(GIT_VERSION)"' $(DEBUG_OPTION) -O3 -OPTIONS=-g -Wno-deprecated-declarations $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(USBOZY_OPTIONS) $(I2C_OPTIONS) $(GPIO_OPTIONS) $(LIMESDR_OPTIONS) $(FREEDV_OPTIONS) $(LOCALCW_OPTIONS) $(RADIOBERRY_OPTIONS) $(PSK_OPTIONS) $(STEMLAB_OPTIONS) -D GIT_DATE='"$(GIT_DATE)"' -D GIT_VERSION='"$(GIT_VERSION)"' $(DEBUG_OPTION) -O3 +LIBS= $(LIBRT) -lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(PSKLIBS) $(GTKLIBS) $(GPIO_LIBS) \ + $(SOAPYSDRLIBS) $(FREEDVLIBS) $(STEMLAB_LIBS) -LIBS=-lrt -lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(PSKLIBS) $(GTKLIBS) $(GPIO_LIBS) $(SOAPYSDRLIBS) $(FREEDVLIBS) $(STEMLAB_LIBS) INCLUDES=$(GTKINCLUDES) COMPILE=$(CC) $(OPTIONS) $(INCLUDES) @@ -213,6 +257,7 @@ dsp_menu.c \ pa_menu.c \ cw_menu.c \ oc_menu.c \ +portaudio.c \ xvtr_menu.c \ equalizer_menu.c \ step_menu.c \ @@ -398,10 +443,11 @@ store_menu.o \ memory.o \ led.o \ ext.o \ -error_handler.o +error_handler.o \ +portaudio.o $(PROGRAM): $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) - $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(LIBS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) + $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) $(LIBS) all: prebuild $(PROGRAM) $(HEADERS) $(REMOTE_HEADERS) $(USBOZY_HEADERS) $(LIMESDR_HEADERS) $(FREEDV_HEADERS) $(LOCALCW_HEADERS) $(I2C_HEADERS) $(GPIO_HEADERS) $(PSK_HEADERS) $(PURESIGNAL_HEADERS) $(STEMLAB_HEADERS) $(SOURCES) $(REMOTE_SOURCES) $(USBOZY_SOURCES) $(LIMESDR_SOURCES) $(FREEDV_SOURCES) $(I2C_SOURCES) $(GPIO_SOURCES) $(PSK_SOURCES) $(PURESIGNAL_SOURCES) $(STEMLAB_SOURCES) @@ -411,6 +457,7 @@ prebuild: clean: -rm -f *.o -rm -f $(PROGRAM) + -rm -rf $(PROGRAM).app install: $(PROGRAM) cp $(PROGRAM) /usr/local/bin @@ -420,3 +467,45 @@ release: $(PROGRAM) cd release; tar cvf pihpsdr.tar pihpsdr cd release; tar cvf pihpsdr-$(GIT_VERSION).tar pihpsdr +############################################################################# +# +# This is for MacOS "app" creation ONLY +# +# Note: Note that we need a wrapper script to start the program, and +# that it requires a working GTK installation on the Mac. +# The program will not work if the +# libgtk, libgdk, libglib, libgobj, libgio libraries +# are copied to the Frameworks dir and "activated", because +# this stuff depends on tons of other files in /usr/local. +# +# We bundle the "app" with the other libraries such as WDSP, +# portaudio, fftw etc. such that the "app" runs on Macs which +# do not have them. But it is *very* hard to do this with GTK. +# +# ATTENTION +# ========= +# Upon starting a freshly built application in an "app" bundle, +# it will do the FFT calculations required for wdspWisdom, since +# this is then stored *within* the app bundle. +# +############################################################################# +app: pihpsdr + @rm -rf pihpsdr.app + @mkdir -p pihpsdr.app/Contents/MacOS + @mkdir -p pihpsdr.app/Contents/Frameworks + @mkdir -p pihpsdr.app/Contents/Resources + @cp pihpsdr pihpsdr.app/Contents/MacOS/pihpsdr-bin + @cp MacOS/PkgInfo pihpsdr.app/Contents + @cp MacOS/Info.plist pihpsdr.app/Contents + @cp MacOS/hpsdr.icns pihpsdr.app/Contents/Resources/hpsdr.icns + @cp MacOS/pihpsdr.sh pihpsdr.app/Contents/MacOS/pihpsdr + @cp MacOS/hpsdr.png pihpsdr.app/Contents/Resources + @for lib in `otool -L pihpsdr.app/Contents/MacOS/pihpsdr-bin | grep dylib | sed -e "s/ (.*//" | grep -Ev "/(usr/lib|System)" | grep -Ev /libg `; do \ + libfn="`basename $$lib`"; \ + cp "$$lib" "pihpsdr.app/Contents/Frameworks/$$libfn"; \ + chmod u+w "pihpsdr.app/Contents/Frameworks/$$libfn"; \ + install_name_tool -id "@executable_path/../Frameworks/$libfn" "pihpsdr.app/Contents/Frameworks/$$libfn"; \ + install_name_tool -change "$$lib" "@executable_path/../Frameworks/$$libfn" pihpsdr.app/Contents/MacOS/pihpsdr-bin; \ + done +############################################################################# + diff --git a/README.MacOS b/README.MacOS new file mode 100644 index 0000000..a43b6a6 --- /dev/null +++ b/README.MacOS @@ -0,0 +1,141 @@ +=============================================================== += = += piHPSDR on the Macintosh. = += = += Port done by DL1YCF Christoph van Wullen. = += = +=============================================================== + + + +============================ +NOTES ON CHANGES IN THE CODE +============================ + +To make piHPSDR work on MacOS, I had to do two major things: + +a) Semaphores: MacOS does not have sem_t variables, only + sem_t pointers that must not be dereferences. Therefore + it has no working sem_init. + On MacOS one must use sem_open instead and use sem_t + pointers instead of sem_t variable throughout. This is + not recommended for LINUX, since named semaphores stay + alive even when the program terminates (on MacOS, all + semaphores are automatically destroyed). Therefore + *every* declaration of sem_t variable and *every* + call to semaphore functions had to be modified. Everything + is coded as + +#ifdef __APPLE__ + use sem_t pointers +#else + usem_t variables +#endif + + NOTE this change also applies to WDSP. + +b) Audio: MacOS does not have ALSA, therefore an additional + file portaudio.c is provided that is a functional duplicate + of audio.c. The whole file audio.c is not "protected with + +#ifndef PORTAUDIO +everything in audio.c +#endif + + and the new file portaudio.c consequently reads + +#ifdef PORTAUDIO +everything in portaudio.c +#endif + + such that one can link and compile both files. As an additional benefit, + the PortAudio module also offeres a two-tone generator as TX "mic" input. + +c) Only relevant for STEMLAB/HAMLAB: the "special" code that starts the + HPSDR application on the RedPitaya board via a browser interface relies + on AVAHI to detect the RedPitaya board. This does not work on MacOS + since we do not have AVAHI. However, libcurl is available on MacOS. + Therefore I provide a stripped-down version in the file stemlab_discovery.c + which assumes that the RedPitaya board is accessible by a fixed + IP address. This address is read from $HOME/.rp.inet and set to 192.168.1.3 + if this file could not be read. + If your STEMlab/HAMlab is then there, the list of applications is obtained + and the HPSDR application with highest priority is started, the priority + defined through the following list (first line = highest priority) + + hamlab_sdr_transceiver_hpsdr + stemlab_sdr_transceiver_hpsdr + sdr_transceiver_hpsdr + sdr_receiver_hpsdr + +============= +PREREQUISITES +============= + +Since Audio and GUI are OpenSource and not Apple-specific, one needs some +third-party libraries to link the program. Most recommended is to use +"homebrew". You will need portaudio and gtk+3 for piHPSDR, and +fftw3 for wdsp (which you have to compile separately). Note that packages +such as gtk+3 depend on other packages, but these are automatically installed +by homebrew. The Makefile itself relies on pkg-config to determine the +compile and link flags for certain external software. + +Of course, you need the Xcode command line tools (make, gcc, and friends). + +Before you compile piHPSDR, you need to compile wdsp. Using the Mac-Makefile +there, "make install" will put libwdsp.dylib in /usr/local/lib. This is needed +by piHPSDR. + +=================== +COMPILE and INSTALL +=================== + + +That's easy. Just adjust the Makefile according to the instructions found there +and type "make". In my case (I have a HAMLAB RedPitaya-based SDR box), I need +the following options in the Makefile (and have all others commented out): + +PORTAUDIO_INCLUDE=PORTAUDIO +STEMLAB_FIX_OPTION=-DSTEMLAB_FIX +STEMLAB_DISCOVERY=STEMLAB_DISCOVERY_MAC +UNAME_N=MacOS + +The first one activates the PortAudio code and disables the Linux ALSA +code, the second option activates some work-arounds around some RedPitaya +HPSDR application bugs (this is also required if you are NOT using MacOS), +and the third option activates code to start the HPSDR program on a RedPitaya +before the actual discovery stuff takes place. + +Note: never un-comment the lines containing LIBRT, GPI_INCLUDE, I2C_INCLUDE, +SX1509_INCLUDE, or LOCALCW_INCLUDE. This software/hardware is not present on a Mac. + +As a result of "make", you get an executable file "pihpsdr" which you +can start from the terminal. With "make app" you can make a click-able +MacOS application bundle. However note that this bundle is not self-contained: +it needs a working gtk+3 environment. To put all this into an app bundle +would create an app file hundreds of MByte long: you need all those pixbuf +loaders etc. etc. etc. installed. + +What we do is to include portaudio, WDSP, fftw3 and some other libs into the bundle. + + +=========================== +piHPSDR and wsjtx or fldigi +=========================== + +a) CAT control: use hamlib, and choose "OpenHPSDR piHPSDR" radio model and + e.g. port number 19090. Then, activate rigctl in the piHPSDR menu, choosing + the same port number there. + +b) Audio: here you need the "SoundFlower" free-software program for MacOS, that + provides virtual audio cables. I have stripped down this somewhat, it now provides + two stero devices (named VAC A and VAC B) and has less overhead, since audio data + is no longer processed (no volume control) but simply moved: after all, this is what + a "cable" is supposed to do. + + For example, you can Choose "VAC A" as the RX output device and "VAC B" as the + TX input (mic) device in piHPSDR, and choose "VAC B" as the output device and "VAC A" + as the input device in fldigi or wsjt-x. + The modified version of SoundFlower is available on my github account, github.com/dl1ycf, + with the name MacOSVirtualAudioCable. + diff --git a/agc_menu.c b/agc_menu.c index f41a5a3..3c9262d 100644 --- a/agc_menu.c +++ b/agc_menu.c @@ -60,6 +60,10 @@ static gboolean agc_select_cb (GtkWidget *widget, gpointer data) { //wdsp_set_agc(CHANNEL_RX0, agc); set_agc(active_receiver, active_receiver->agc); 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 923685a..f6a7439 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 48ed0b9..cbf606f 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 514e77d..325c249 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 5c7313e..800b04d 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 071736b..c5ba619 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 aee2b68..092d890 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 8028ad0..0370ff7 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 51026b5..71f2502 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 b17a210..4e3bcbc 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 b2c02bb..d40eee3 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 8ab5f88..634d99d 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 78da6c8..93d84a9 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 fa09067..659c721 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 ea4daf8..939ef04 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 57ea09e..e47c465 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 3459bec..8b2aa12 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 0000000..69f77fc --- /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 658c8f5..512687b 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 3bd59c4..39bf084 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 6216fb3..c121392 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 704de48..3e83bb1 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 1927884..e3ceb7d 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 d1d0a87..233a5b0 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 80f3871..2bb38a9 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 d2c12ec..aeac250 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 a746fe1..7e08fc9 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 eaf6e8f..43f6e5c 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 61262d8..a279a3a 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 e2356ea..e47936d 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 460ac43..e14d5f2 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 b673f80..bccebcc 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