From 34eaf8a427efb53e0edcd01fbb976ea01d7b6108 Mon Sep 17 00:00:00 2001 From: John Melton g0orx/n6lyt Date: Wed, 6 Apr 2016 07:10:52 +0000 Subject: [PATCH] changes for updated wdsp and some cleanup --- Makefile | 45 +- agc.h | 19 + alex.h | 19 + band.c | 81 ++- band.h | 27 +- bandstack.h | 13 +- channel.h | 19 + discovered.c | 25 + discovered.h | 19 + filter.c | 19 + filter.h | 11 +- gpio.c | 1147 +++++++++++++++++++++++++++++++++++++ gpio.h | 71 +++ main.c | 840 +++++++++++++++++++++++---- main.h | 19 + menu.c | 532 +++++++++++++++++ menu.h | 21 + meter.c | 108 +++- meter.h | 21 +- mode.c | 19 + mode.h | 12 +- new_discovery.c | 27 +- new_discovery.h | 19 + new_protocol.c | 392 +++---------- new_protocol.h | 60 +- new_protocol_programmer.c | 19 + old_discovery.c | 251 ++++++++ old_discovery.h | 20 + old_protocol.c | 803 ++++++++++++++++++++++++++ old_protocol.h | 23 + panadapter.c | 85 ++- panadapter.h | 19 + pihpsdr | Bin 200890 -> 0 bytes property.c | 19 + property.h | 19 + radio.c | 260 ++++++++- radio.h | 119 +++- rotary_encoder.c | 298 ---------- rotary_encoder.h | 6 - splash.c | 78 ++- splash.h | 22 +- splash.png | Bin 19869 -> 0 bytes toolbar.c | 895 +++++++++++++++-------------- toolbar.h | 24 + version.c | 22 + version.h | 22 + vfo.c | 277 +++------ vfo.h | 19 + waterfall.c | 38 +- waterfall.h | 19 + wdsp_init.c | 280 +++++++++ wdsp_init.h | 25 + xvtr.h | 13 +- 53 files changed, 5732 insertions(+), 1528 deletions(-) create mode 100644 discovered.c create mode 100644 gpio.c create mode 100644 gpio.h create mode 100644 menu.c create mode 100644 menu.h create mode 100644 old_discovery.c create mode 100644 old_discovery.h create mode 100644 old_protocol.c create mode 100644 old_protocol.h delete mode 100755 pihpsdr delete mode 100644 rotary_encoder.c delete mode 100644 rotary_encoder.h delete mode 100644 splash.png create mode 100644 version.c create mode 100644 version.h create mode 100644 wdsp_init.c create mode 100644 wdsp_init.h diff --git a/Makefile b/Makefile index c5bae3b..7b468b0 100644 --- a/Makefile +++ b/Makefile @@ -2,10 +2,14 @@ UNAME_N := $(shell uname -n) CC=gcc LINK=gcc -OPTIONS=-g -D $(UNAME_N) +OPTIONS=-g -D $(UNAME_N) -O3 GTKINCLUDES=`pkg-config --cflags gtk+-3.0` GTKLIBS=`pkg-config --libs gtk+-3.0` +ifeq ($(UNAME_N),raspberrypi) LIBS=-lwiringPi -lpigpio -lrt -lm -lwdsp -lpthread $(GTKLIBS) +else +LIBS=-lwiringPi -lrt -lm -lwdsp -lpthread $(GTKLIBS) +endif INCLUDES=$(GTKINCLUDES) COMPILE=$(CC) $(OPTIONS) $(INCLUDES) @@ -15,72 +19,97 @@ PROGRAM=pihpsdr SOURCES= \ band.c \ +frequency.c \ +discovered.c \ filter.c \ main.c \ +menu.c \ meter.c \ mode.c \ +old_discovery.c \ new_discovery.c \ +old_protocol.c \ new_protocol.c \ new_protocol_programmer.c \ panadapter.c \ property.c \ radio.c \ -rotary_encoder.c \ +gpio.c \ splash.c \ toolbar.c \ +version.c \ vfo.c \ -waterfall.c +waterfall.c \ +wdsp_init.c HEADERS= \ agc.h \ alex.h \ band.h \ +frequency.h \ bandstack.h \ channel.h \ discovered.h \ filter.h \ +menu.h \ meter.h \ mode.h \ +old_discovery.h \ new_discovery.h \ +old_protocol.h \ new_protocol.h \ panadapter.h \ property.h \ radio.h \ -rotary_encoder.h \ +gpio.h \ splash.h \ toolbar.h \ +version.h \ vfo.h \ waterfall.h \ +wdsp_init.h \ xvtr.h OBJS= \ band.o \ +frequency.o \ +discovered.o \ filter.o \ +version.o \ main.o \ +menu.o \ meter.o \ mode.o \ +old_discovery.o \ new_discovery.o \ +old_protocol.o \ new_protocol.o \ new_protocol_programmer.o \ panadapter.o \ property.o \ radio.o \ -rotary_encoder.o \ +gpio.o \ splash.o \ toolbar.o \ vfo.o \ -waterfall.o +waterfall.o \ +wdsp_init.o -all: $(PROGRAM) $(HEADERS) $(SOURCES) +all: prebuild $(PROGRAM) $(HEADERS) $(SOURCES) + +prebuild: + rm -f version.o $(PROGRAM): $(OBJS) $(LINK) -o $(PROGRAM) $(OBJS) $(LIBS) .c.o: - $(COMPILE) $(OPTIONS) -c -o $@ $< + $(COMPILE) -c -o $@ $< clean: -rm -f *.o -rm -f $(PROGRAM) +install: + cp pihpsdr ../pihpsdr diff --git a/agc.h b/agc.h index c6e3aff..c64de97 100644 --- a/agc.h +++ b/agc.h @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #define AGC_OFF 0 #define AGC_LONG 1 #define AGC_SLOW 2 diff --git a/alex.h b/alex.h index 7ead806..a9178cb 100644 --- a/alex.h +++ b/alex.h @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #define ALEX_RX_ANTENNA_NONE 0x00000000 #define ALEX_RX_ANTENNA_XVTR 0x00000900 #define ALEX_RX_ANTENNA_EXT1 0x00000A00 diff --git a/band.c b/band.c index 53ec328..d023d04 100644 --- a/band.c +++ b/band.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #include "bandstack.h" @@ -100,19 +119,19 @@ BANDSTACK bandstackGEN={3,1,bandstack_entriesGEN}; BANDSTACK bandstackWWV={5,1,bandstack_entriesWWV}; BAND bands[BANDS] = - {{"160",&bandstack160,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"80",&bandstack80,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"60",&bandstack60,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"40",&bandstack40,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"30",&bandstack30,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"20",&bandstack20,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"18",&bandstack18,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"15",&bandstack15,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"12",&bandstack12,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"10",&bandstack10,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"50",&bandstack50,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"GEN",&bandstackGEN,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}, - {"WWV",&bandstackWWV,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}}; + {{"160",&bandstack160,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"80",&bandstack80,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"60",&bandstack60,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"40",&bandstack40,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"30",&bandstack30,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"20",&bandstack20,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"18",&bandstack18,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"15",&bandstack15,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"12",&bandstack12,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"10",&bandstack10,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"50",&bandstack50,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"GEN",&bandstackGEN,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,0}, + {"WWV",&bandstackWWV,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,0}}; #define NUM_BAND_LIMITS 22 @@ -170,13 +189,24 @@ BANDSTACK_ENTRY *bandstack_entry_get_current() { BANDSTACK_ENTRY *bandstack_entry_next() { BANDSTACK *bandstack=bands[band].bandstack; bandstack->current_entry++; - if(bandstack->current_entry==bandstack->entries) { + if(bandstack->current_entry>=bandstack->entries) { bandstack->current_entry=0; } BANDSTACK_ENTRY *entry=&bandstack->entry[bandstack->current_entry]; return entry; } +BANDSTACK_ENTRY *bandstack_entry_previous() { + BANDSTACK *bandstack=bands[band].bandstack; + bandstack->current_entry--; + if(bandstack->current_entry<0) { + bandstack->current_entry=bandstack->entries-1; + } + BANDSTACK_ENTRY *entry=&bandstack->entry[bandstack->current_entry]; + return entry; +} + + int band_get_current() { return band; } @@ -230,6 +260,10 @@ void bandSaveState() { sprintf(name,"band.%d.alexAttenuation",b); setProperty(name,value); + sprintf(value,"%d",bands[b].pa_calibration); + sprintf(name,"band.%d.pa_calibration",b); + setProperty(name,value); + for(stack=0;stackentries;stack++) { entry=bands[b].bandstack->entry; entry+=stack; @@ -303,6 +337,11 @@ void bandRestoreState() { sprintf(name,"band.%d.alexAttenuation",b); value=getProperty(name); if(value) bands[b].alexAttenuation=atoi(value); + + sprintf(name,"band.%d.pa_calibration",b); + value=getProperty(name); + if(value) bands[b].pa_calibration=atoi(value); + for(stack=0;stackentries;stack++) { entry=bands[b].bandstack->entry; entry+=stack; @@ -341,4 +380,18 @@ void bandRestoreState() { if(value) band=atoi(value); } +BAND_LIMITS* getBandLimits(long long minDisplay,long long maxDisplay) { + BAND_LIMITS* limits; + int i; + + for(i=0;iminFrequency&&maxDisplay>=limits->minFrequency) || + (minDisplay<=limits->maxFrequency&&maxDisplay>=limits->maxFrequency)) { + return limits; + } + } + + return NULL; +} diff --git a/band.h b/band.h index 24d15c1..add9288 100644 --- a/band.h +++ b/band.h @@ -1,14 +1,6 @@ -/** -* @file band.h -* @brief Header files for the Amateur Radio band stack. -* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S -* @version 0.1 -* @date 2009-04-11 -*/ -// band.h - /* Copyright (C) -* 2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S +* 2015 - John Melton, G0ORX/N6LYT +* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 @@ -43,6 +35,7 @@ #define bandWWV 12 #define BANDS 13 +#define HAM_BANDS 11 /* --------------------------------------------------------------------------*/ /** @@ -68,6 +61,7 @@ struct _BAND { unsigned long alexRxAntenna; unsigned long alexTxAntenna; unsigned long alexAttenuation; + int pa_calibration; }; typedef struct _BAND BAND; @@ -82,19 +76,12 @@ BAND *band_get_band(int b); BAND *band_set_current(int b); BANDSTACK_ENTRY *bandstack_entry_next(); +BANDSTACK_ENTRY *bandstack_entry_previous(); BANDSTACK_ENTRY *bandstack_entry_get_current(); -/* -void bandSaveState(); -void bandRestoreState(); -void forceBand(int band,int setup); -void configureXVTRButton(int setup); -GtkWidget* buildBandUI(); - -int remoteSetBand(gpointer *data); - BAND_LIMITS* getBandLimits(long long minDisplay,long long maxDisplay); -XVTR_ENTRY* getXvtrEntry(int i); +/* +XVTR_ENTRY* getXvtrEntry(int i); */ diff --git a/bandstack.h b/bandstack.h index 2fb8e9a..c40a09e 100644 --- a/bandstack.h +++ b/bandstack.h @@ -1,14 +1,7 @@ -/** -* @file bandstack.h -* @brief Bandstack definition files -* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S -* @version 0.1 -* @date 2009-04-11 -*/ -// bandstack.h - /* Copyright (C) -* This program is free software; you can redistribute it and/or2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. diff --git a/channel.h b/channel.h index ae1e178..877f7a0 100644 --- a/channel.h +++ b/channel.h @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #define CHANNEL_RX0 0 #define CHANNEL_RX1 1 #define CHANNEL_RX2 2 diff --git a/discovered.c b/discovered.c new file mode 100644 index 0000000..513f255 --- /dev/null +++ b/discovered.c @@ -0,0 +1,25 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +#include "discovered.h" + +int selected_device=0; +int devices=0; +DISCOVERED discovered[MAX_DEVICES]; + diff --git a/discovered.h b/discovered.h index e644e69..6eea262 100644 --- a/discovered.h +++ b/discovered.h @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #define MAX_DEVICES 16 diff --git a/filter.c b/filter.c index e6b0fe5..c6a4ff8 100644 --- a/filter.c +++ b/filter.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include "filter.h" FILTER filterLSB[FILTERS]={ diff --git a/filter.h b/filter.h index ad72c9f..c6e0503 100644 --- a/filter.h +++ b/filter.h @@ -1,13 +1,6 @@ -/** -* @file filter.h -* @brief Header files to define the filters. -* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S -* @version 0.1 -* @date 2009-04-11 -*/ -// filter.h /* Copyright (C) -* 2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S +* 2015 - John Melton, G0ORX/N6LYT +* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 diff --git a/gpio.c b/gpio.c new file mode 100644 index 0000000..ccc35ba --- /dev/null +++ b/gpio.c @@ -0,0 +1,1147 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef raspberrypi +#include +#endif + +#include "band.h" +#include "channel.h" +#include "mode.h" +#include "filter.h" +#include "bandstack.h" +#include "toolbar.h" +#include "gpio.h" +#include "radio.h" +#include "toolbar.h" +#include "main.h" +#include "property.h" + +#define SYSFS_GPIO_DIR "/sys/class/gpio" + +int ENABLE_VFO_ENCODER=1; +int ENABLE_VFO_PULLUP=1; +#ifdef raspberrypi +int VFO_ENCODER_A=17; +int VFO_ENCODER_B=18; +#endif +#ifdef odroid +int VFO_ENCODER_A=88; +int VFO_ENCODER_B=87; +int VFO_ENCODER_A_PIN=0; +int VFO_ENCODER_B_PIN=1; +#endif +int ENABLE_AF_ENCODER=88; +int ENABLE_AF_PULLUP=87; +int AF_ENCODER_A=20; +int AF_ENCODER_B=26; +int AF_FUNCTION=25; +int ENABLE_RF_ENCODER=1; +int ENABLE_RF_PULLUP=0; +int RF_ENCODER_A=16; +int RF_ENCODER_B=19; +int RF_FUNCTION=8; +int ENABLE_AGC_ENCODER=1; +int ENABLE_AGC_PULLUP=0; +int AGC_ENCODER_A=4; +int AGC_ENCODER_B=21; +int AGC_FUNCTION=7; +int ENABLE_BAND_BUTTON=1; +int BAND_BUTTON=13; +int ENABLE_BANDSTACK_BUTTON=1; +int BANDSTACK_BUTTON=12; +int ENABLE_MODE_BUTTON=1; +int MODE_BUTTON=6; +int ENABLE_FILTER_BUTTON=1; +int FILTER_BUTTON=5; +int ENABLE_NOISE_BUTTON=1; +int NOISE_BUTTON=24; +int ENABLE_AGC_BUTTON=1; +int AGC_BUTTON=23; +int ENABLE_MOX_BUTTON=1; +int MOX_BUTTON=27; +int ENABLE_FUNCTION_BUTTON=1; +int FUNCTION_BUTTON=22; +int ENABLE_LOCK_BUTTON=1; +int LOCK_BUTTON=25; + + + +/* +#define PI_VFO_ENCODER_A 17 +#define PI_VFO_ENCODER_B 18 + +#define ODROID_VFO_ENCODER_A 88 +#define ODROID_VFO_ENCODER_B 87 +#define ODROID_VFO_ENCODER_A_PIN 0 +#define ODROID_VFO_ENCODER_B_PIN 1 + +#define AF_ENCODER_A 20 +#define AF_ENCODER_B 26 +#define AF_FUNCTION 21 + +#define RF_ENCODER_A 16 +#define RF_ENCODER_B 19 +#define RF_FUNCTION 13 + +#define FUNCTION_BUTTON 22 +#define BAND_BUTTON 13 +#define BANDSTACK_BUTTON 12 +#define MODE_BUTTON 6 +#define FILTER_BUTTON 5 +#define NOISE_BUTTON 24 +#define AGC_BUTTON 23 +#define MOX_BUTTON 27 + +static int VFO_ENCODER_A=17; +static int VFO_ENCODER_B=18; + +static int VFO_ENCODER_A_PIN=0; +static int VFO_ENCODER_B_PIN=0; +*/ + +static volatile int vfoEncoderPos; +static volatile int afEncoderPos; +static volatile int afFunction; +static volatile int rfEncoderPos; +static volatile int rfFunction; +static volatile int agcEncoderPos; +static volatile int function_state; +static volatile int band_state; +static volatile int bandstack_state; +static volatile int mode_state; +static volatile int filter_state; +static volatile int noise_state; +static volatile int agc_state; +static volatile int mox_state; +static volatile int lock_state; + +static void* rotary_encoder_thread(void *arg); +static pthread_t rotary_encoder_thread_id; +int function=0; +static int previous_function_button=0; +static int af_function=0; +static int previous_af_function=0; +static int rf_function=0; +static int previous_rf_function=0; +static int band_button=0; +static int previous_band_button=0; +static int bandstack_button=0; +static int previous_bandstack_button=0; +static int mode_button=0; +static int previous_mode_button=0; +static int filter_button=0; +static int previous_filter_button=0; +static int noise_button=0; +static int previous_noise_button=0; +static int agc_button=0; +static int previous_agc_button=0; +static int mox_button=0; +static int previous_mox_button=0; +static int lock_button=0; +static int previous_lock_button=0; + +static void afFunctionAlert(int gpio, int level, uint32_t tick) { + afFunction=(level==0); +} + +static void rfFunctionAlert(int gpio, int level, uint32_t tick) { + rfFunction=(level==0); +} + +static void functionAlert(int gpio, int level, uint32_t tick) { + function_state=(level==0); +} + +static void bandAlert(int gpio, int level, uint32_t tick) { + band_state=(level==0); +} + +static void bandstackAlert(int gpio, int level, uint32_t tick) { + bandstack_state=(level==0); +} + +static void modeAlert(int gpio, int level, uint32_t tick) { + mode_state=(level==0); +} + +static void filterAlert(int gpio, int level, uint32_t tick) { + filter_state=(level==0); +} + +static void noiseAlert(int gpio, int level, uint32_t tick) { + noise_state=(level==0); +} + +static void agcAlert(int gpio, int level, uint32_t tick) { + agc_state=(level==0); +} + +static void moxAlert(int gpio, int level, uint32_t tick) { + mox_state=(level==0); +} + +static void lockAlert(int gpio, int level, uint32_t tick) { + lock_state=(level==0); +} + +static void vfoEncoderPulse(int gpio, int level, unsigned int tick) { + static int levA=0, levB=0, lastGpio = -1; + + if (gpio == VFO_ENCODER_A) levA = level; else levB = level; + + if (gpio != lastGpio) /* debounce */ + { + lastGpio = gpio; + + if ((gpio == VFO_ENCODER_A) && (level == 0)) + { + if (!levB) ++vfoEncoderPos; + } + else if ((gpio == VFO_ENCODER_B) && (level == 1)) + { + if (levA) --vfoEncoderPos; + } + } +} + +static void afEncoderPulse(int gpio, int level, uint32_t tick) +{ + static int levA=0, levB=0, lastGpio = -1; + + if (gpio == AF_ENCODER_A) levA = level; else levB = level; + + if (gpio != lastGpio) /* debounce */ + { + lastGpio = gpio; + + if ((gpio == AF_ENCODER_A) && (level == 0)) + { + if (!levB) ++afEncoderPos; + } + else if ((gpio == AF_ENCODER_B) && (level == 1)) + { + if (levA) --afEncoderPos; + } + } +} + +static void rfEncoderPulse(int gpio, int level, uint32_t tick) +{ + static int levA=0, levB=0, lastGpio = -1; + + if (gpio == RF_ENCODER_A) levA = level; else levB = level; + + if (gpio != lastGpio) /* debounce */ + { + lastGpio = gpio; + + if ((gpio == RF_ENCODER_A) && (level == 0)) + { + if (!levB) ++rfEncoderPos; + } + else if ((gpio == RF_ENCODER_B) && (level == 1)) + { + if (levA) --rfEncoderPos; + } + } +} + +static void agcEncoderPulse(int gpio, int level, uint32_t tick) +{ + static int levA=0, levB=0, lastGpio = -1; + + if (gpio == AGC_ENCODER_A) levA = level; else levB = level; + + if (gpio != lastGpio) /* debounce */ + { + lastGpio = gpio; + + if ((gpio == AGC_ENCODER_A) && (level == 0)) + { + if (!levB) ++agcEncoderPos; + } + else if ((gpio == AGC_ENCODER_B) && (level == 1)) + { + if (levA) --agcEncoderPos; + } + } +} + +#ifdef odroid +void interruptB(void) { + vfoEncoderPulse(VFO_ENCODER_B,digitalRead(VFO_ENCODER_B_PIN),0); +} + +void interruptA(void) { + vfoEncoderPulse(VFO_ENCODER_A,digitalRead(VFO_ENCODER_A_PIN),0); +} +#endif + +void gpio_restore_state() { + char* value; + loadProperties("gpio.props"); + value=getProperty("ENABLE_VFO_ENCODER"); + if(value) ENABLE_VFO_ENCODER=atoi(value); + value=getProperty("ENABLE_VFO_PULLUP"); + if(value) ENABLE_VFO_PULLUP=atoi(value); + value=getProperty("VFO_ENCODER_A"); + if(value) VFO_ENCODER_A=atoi(value); + value=getProperty("VFO_ENCODER_B"); + if(value) VFO_ENCODER_B=atoi(value); +#ifdef odroid + value=getProperty("VFO_ENCODER_A_PIN"); + if(value) VFO_ENCODER_A_PIN=atoi(value); + value=getProperty("VFO_ENCODER_B_PIN"); + if(value) VFO_ENCODER_B_PIN=atoi(value); +#endif + value=getProperty("ENABLE_AF_ENCODER"); + if(value) ENABLE_AF_ENCODER=atoi(value); + value=getProperty("ENABLE_AF_PULLUP"); + if(value) ENABLE_AF_PULLUP=atoi(value); + value=getProperty("AF_ENCODER_A"); + if(value) AF_ENCODER_A=atoi(value); + value=getProperty("AF_ENCODER_B"); + if(value) AF_ENCODER_B=atoi(value); + value=getProperty("ENABLE_RF_ENCODER"); + if(value) ENABLE_RF_ENCODER=atoi(value); + value=getProperty("ENABLE_RF_PULLUP"); + if(value) ENABLE_RF_PULLUP=atoi(value); + value=getProperty("RF_ENCODER_A"); + if(value) RF_ENCODER_A=atoi(value); + value=getProperty("RF_ENCODER_B"); + if(value) RF_ENCODER_B=atoi(value); + value=getProperty("ENABLE_AGC_ENCODER"); + if(value) ENABLE_AGC_ENCODER=atoi(value); + value=getProperty("ENABLE_AGC_PULLUP"); + if(value) ENABLE_AGC_PULLUP=atoi(value); + value=getProperty("AGC_ENCODER_A"); + if(value) AGC_ENCODER_A=atoi(value); + value=getProperty("AGC_ENCODER_B"); + if(value) AGC_ENCODER_B=atoi(value); + value=getProperty("ENABLE_BAND_BUTTON"); + if(value) ENABLE_BAND_BUTTON=atoi(value); + value=getProperty("BAND_BUTTON"); + if(value) BAND_BUTTON=atoi(value); + value=getProperty("ENABLE_BANDSTACK_BUTTON"); + if(value) ENABLE_BANDSTACK_BUTTON=atoi(value); + value=getProperty("BANDSTACK_BUTTON"); + if(value) BANDSTACK_BUTTON=atoi(value); + value=getProperty("ENABLE_MODE_BUTTON"); + if(value) ENABLE_MODE_BUTTON=atoi(value); + value=getProperty("MODE_BUTTON"); + if(value) MODE_BUTTON=atoi(value); + value=getProperty("ENABLE_FILTER_BUTTON"); + if(value) ENABLE_FILTER_BUTTON=atoi(value); + value=getProperty("FILTER_BUTTON"); + if(value) FILTER_BUTTON=atoi(value); + value=getProperty("ENABLE_NOISE_BUTTON"); + if(value) ENABLE_NOISE_BUTTON=atoi(value); + value=getProperty("NOISE_BUTTON"); + if(value) NOISE_BUTTON=atoi(value); + value=getProperty("ENABLE_AGC_BUTTON"); + if(value) ENABLE_AGC_BUTTON=atoi(value); + value=getProperty("AGC_BUTTON"); + if(value) AGC_BUTTON=atoi(value); + value=getProperty("ENABLE_FUNCTION_BUTTON"); + if(value) ENABLE_FUNCTION_BUTTON=atoi(value); + value=getProperty("FUNCTION_BUTTON"); + if(value) FUNCTION_BUTTON=atoi(value); + value=getProperty("ENABLE_MOX_BUTTON"); + if(value) ENABLE_MOX_BUTTON=atoi(value); + value=getProperty("MOX_BUTTON"); + if(value) MOX_BUTTON=atoi(value); + value=getProperty("ENABLE_LOCK_BUTTON"); + if(value) ENABLE_LOCK_BUTTON=atoi(value); + value=getProperty("LOCK_BUTTON"); + if(value) LOCK_BUTTON=atoi(value); +} + +void gpio_save_state() { + char value[80]; + + sprintf(value,"%d",ENABLE_VFO_ENCODER); + setProperty("ENABLE_VFO_ENCODER",value); + sprintf(value,"%d",ENABLE_VFO_PULLUP); + setProperty("ENABLE_VFO_PULLUP",value); + sprintf(value,"%d",VFO_ENCODER_A); + setProperty("VFO_ENCODER_A",value); + sprintf(value,"%d",VFO_ENCODER_B); + setProperty("VFO_ENCODER_B",value); +#ifdef odroid + sprintf(value,"%d",VFO_ENCODER_A_PIN); + setProperty("VFO_ENCODER_A_PIN",value); + sprintf(value,"%d",VFO_ENCODER_B_PIN); + setProperty("VFO_ENCODER_B_PIN",value); +#endif + sprintf(value,"%d",ENABLE_AF_ENCODER); + setProperty("ENABLE_AF_ENCODER",value); + sprintf(value,"%d",ENABLE_AF_PULLUP); + setProperty("ENABLE_AF_PULLUP",value); + sprintf(value,"%d",AF_ENCODER_A); + setProperty("AF_ENCODER_A",value); + sprintf(value,"%d",AF_ENCODER_B); + setProperty("AF_ENCODER_B",value); + sprintf(value,"%d",ENABLE_RF_ENCODER); + setProperty("ENABLE_RF_ENCODER",value); + sprintf(value,"%d",ENABLE_RF_PULLUP); + setProperty("ENABLE_RF_PULLUP",value); + sprintf(value,"%d",RF_ENCODER_A); + setProperty("RF_ENCODER_A",value); + sprintf(value,"%d",RF_ENCODER_B); + setProperty("RF_ENCODER_B",value); + sprintf(value,"%d",ENABLE_AGC_ENCODER); + setProperty("ENABLE_AGC_ENCODER",value); + sprintf(value,"%d",ENABLE_AGC_PULLUP); + setProperty("ENABLE_AGC_PULLUP",value); + sprintf(value,"%d",AGC_ENCODER_A); + setProperty("AGC_ENCODER_A",value); + sprintf(value,"%d",AGC_ENCODER_B); + setProperty("AGC_ENCODER_B",value); + sprintf(value,"%d",ENABLE_BAND_BUTTON); + setProperty("ENABLE_BAND_BUTTON",value); + sprintf(value,"%d",BAND_BUTTON); + setProperty("BAND_BUTTON",value); + sprintf(value,"%d",ENABLE_BANDSTACK_BUTTON); + setProperty("ENABLE_BANDSTACK_BUTTON",value); + sprintf(value,"%d",BANDSTACK_BUTTON); + setProperty("BANDSTACK_BUTTON",value); + sprintf(value,"%d",ENABLE_MODE_BUTTON); + setProperty("ENABLE_MODE_BUTTON",value); + sprintf(value,"%d",MODE_BUTTON); + setProperty("MODE_BUTTON",value); + sprintf(value,"%d",ENABLE_FILTER_BUTTON); + setProperty("ENABLE_FILTER_BUTTON",value); + sprintf(value,"%d",FILTER_BUTTON); + setProperty("FILTER_BUTTON",value); + sprintf(value,"%d",ENABLE_NOISE_BUTTON); + setProperty("ENABLE_NOISE_BUTTON",value); + sprintf(value,"%d",NOISE_BUTTON); + setProperty("NOISE_BUTTON",value); + sprintf(value,"%d",ENABLE_AGC_BUTTON); + setProperty("ENABLE_AGC_BUTTON",value); + sprintf(value,"%d",AGC_BUTTON); + setProperty("AGC_BUTTON",value); + sprintf(value,"%d",ENABLE_FUNCTION_BUTTON); + setProperty("ENABLE_FUNCTION_BUTTON",value); + sprintf(value,"%d",FUNCTION_BUTTON); + setProperty("FUNCTION_BUTTON",value); + sprintf(value,"%d",ENABLE_MOX_BUTTON); + setProperty("ENABLE_MOX_BUTTON",value); + sprintf(value,"%d",MOX_BUTTON); + setProperty("MOX_BUTTON",value); + sprintf(value,"%d",ENABLE_LOCK_BUTTON); + setProperty("ENABLE_LOCK_BUTTON",value); + sprintf(value,"%d",LOCK_BUTTON); + setProperty("LOCK_BUTTON",value); + + saveProperties("gpio.props"); +} + + +int gpio_init() { +fprintf(stderr,"encoder_init\n"); + gpio_restore_state(); + //if(strcmp(unameData.nodename,"raspberrypi")==0) { +#ifdef raspberrypi + + fprintf(stderr,"encoder_init: VFO_ENCODER_A=%d VFO_ENCODER_B=%d\n",VFO_ENCODER_A,VFO_ENCODER_B); + + fprintf(stderr,"gpioInitialize\n"); + if(gpioInitialise()<0) { + fprintf(stderr,"Cannot initialize GPIO\n"); + return -1; + } + + if(ENABLE_FUNCTION_BUTTON) { + gpioSetMode(FUNCTION_BUTTON, PI_INPUT); + gpioSetPullUpDown(FUNCTION_BUTTON,PI_PUD_UP); + gpioSetAlertFunc(FUNCTION_BUTTON, functionAlert); + } + + if(ENABLE_VFO_ENCODER) { + gpioSetMode(VFO_ENCODER_A, PI_INPUT); + gpioSetMode(VFO_ENCODER_B, PI_INPUT); + if(ENABLE_VFO_PULLUP) { + gpioSetPullUpDown(VFO_ENCODER_A, PI_PUD_UP); + gpioSetPullUpDown(VFO_ENCODER_B, PI_PUD_UP); + } else { + gpioSetPullUpDown(VFO_ENCODER_A, PI_PUD_OFF); + gpioSetPullUpDown(VFO_ENCODER_B, PI_PUD_OFF); + } + gpioSetAlertFunc(VFO_ENCODER_A, vfoEncoderPulse); + gpioSetAlertFunc(VFO_ENCODER_B, vfoEncoderPulse); + vfoEncoderPos=0; + } + + + gpioSetMode(AF_FUNCTION, PI_INPUT); + gpioSetPullUpDown(AF_FUNCTION,PI_PUD_UP); + gpioSetAlertFunc(AF_FUNCTION, afFunctionAlert); + afFunction=0; + + if(ENABLE_AF_ENCODER) { + gpioSetMode(AF_ENCODER_A, PI_INPUT); + gpioSetMode(AF_ENCODER_B, PI_INPUT); + if(ENABLE_AF_PULLUP) { + gpioSetPullUpDown(AF_ENCODER_A, PI_PUD_UP); + gpioSetPullUpDown(AF_ENCODER_B, PI_PUD_UP); + } else { + gpioSetPullUpDown(AF_ENCODER_A, PI_PUD_OFF); + gpioSetPullUpDown(AF_ENCODER_B, PI_PUD_OFF); + } + gpioSetAlertFunc(AF_ENCODER_A, afEncoderPulse); + gpioSetAlertFunc(AF_ENCODER_B, afEncoderPulse); + afEncoderPos=0; + } + + gpioSetMode(RF_FUNCTION, PI_INPUT); + gpioSetPullUpDown(RF_FUNCTION,PI_PUD_UP); + gpioSetAlertFunc(RF_FUNCTION, rfFunctionAlert); + rfFunction=0; + + if(ENABLE_RF_ENCODER) { + gpioSetMode(RF_ENCODER_A, PI_INPUT); + gpioSetMode(RF_ENCODER_B, PI_INPUT); + if(ENABLE_AF_PULLUP) { + gpioSetPullUpDown(RF_ENCODER_A, PI_PUD_UP); + gpioSetPullUpDown(RF_ENCODER_B, PI_PUD_UP); + } else { + gpioSetPullUpDown(RF_ENCODER_A, PI_PUD_OFF); + gpioSetPullUpDown(RF_ENCODER_B, PI_PUD_OFF); + } + gpioSetAlertFunc(RF_ENCODER_A, rfEncoderPulse); + gpioSetAlertFunc(RF_ENCODER_B, rfEncoderPulse); + rfEncoderPos=0; + } + + if(ENABLE_AGC_ENCODER) { + gpioSetMode(AGC_ENCODER_A, PI_INPUT); + gpioSetMode(AGC_ENCODER_B, PI_INPUT); + if(ENABLE_AF_PULLUP) { + gpioSetPullUpDown(AGC_ENCODER_A, PI_PUD_UP); + gpioSetPullUpDown(AGC_ENCODER_B, PI_PUD_UP); + } else { + gpioSetPullUpDown(AGC_ENCODER_A, PI_PUD_OFF); + gpioSetPullUpDown(AGC_ENCODER_B, PI_PUD_OFF); + } + gpioSetAlertFunc(AGC_ENCODER_A, agcEncoderPulse); + gpioSetAlertFunc(AGC_ENCODER_B, agcEncoderPulse); + rfEncoderPos=0; + } + + + if(ENABLE_BAND_BUTTON) { + gpioSetMode(BAND_BUTTON, PI_INPUT); + gpioSetPullUpDown(BAND_BUTTON,PI_PUD_UP); + gpioSetAlertFunc(BAND_BUTTON, bandAlert); + } + + if(ENABLE_BANDSTACK_BUTTON) { + gpioSetMode(BANDSTACK_BUTTON, PI_INPUT); + gpioSetPullUpDown(BANDSTACK_BUTTON,PI_PUD_UP); + gpioSetAlertFunc(BANDSTACK_BUTTON, bandstackAlert); + } + + if(ENABLE_MODE_BUTTON) { + gpioSetMode(MODE_BUTTON, PI_INPUT); + gpioSetPullUpDown(MODE_BUTTON,PI_PUD_UP); + gpioSetAlertFunc(MODE_BUTTON, modeAlert); + } + + if(ENABLE_FILTER_BUTTON) { + gpioSetMode(FILTER_BUTTON, PI_INPUT); + gpioSetPullUpDown(FILTER_BUTTON,PI_PUD_UP); + gpioSetAlertFunc(FILTER_BUTTON, filterAlert); + } + + if(ENABLE_NOISE_BUTTON) { + gpioSetMode(NOISE_BUTTON, PI_INPUT); + gpioSetPullUpDown(NOISE_BUTTON,PI_PUD_UP); + gpioSetAlertFunc(NOISE_BUTTON, noiseAlert); + } + + if(ENABLE_AGC_BUTTON) { + gpioSetMode(AGC_BUTTON, PI_INPUT); + gpioSetPullUpDown(AGC_BUTTON,PI_PUD_UP); + gpioSetAlertFunc(AGC_BUTTON, agcAlert); + } + + if(ENABLE_MOX_BUTTON) { + gpioSetMode(MOX_BUTTON, PI_INPUT); + gpioSetPullUpDown(MOX_BUTTON,PI_PUD_UP); + gpioSetAlertFunc(MOX_BUTTON, moxAlert); + } + + if(ENABLE_LOCK_BUTTON) { + gpioSetMode(LOCK_BUTTON, PI_INPUT); + gpioSetPullUpDown(LOCK_BUTTON,PI_PUD_UP); + gpioSetAlertFunc(LOCK_BUTTON, lockAlert); + } + +#endif +// } else if(strcmp(unameData.nodename,"odroid")==0) { +#ifdef odroid + + //VFO_ENCODER_A=ODROID_VFO_ENCODER_A; + //VFO_ENCODER_B=ODROID_VFO_ENCODER_B; + //VFO_ENCODER_A_PIN=ODROID_VFO_ENCODER_A_PIN; + //VFO_ENCODER_B_PIN=ODROID_VFO_ENCODER_B_PIN; + + fprintf(stderr,"encoder_init: VFO_ENCODER_A=%d VFO_ENCODER_B=%d\n",VFO_ENCODER_A,VFO_ENCODER_B); + + fprintf(stderr,"wiringPiSetup\n"); + if (wiringPiSetup () < 0) { + printf ("Unable to setup wiringPi: %s\n", strerror (errno)); + return 1; + } + + FILE *fp; + + fp = popen("echo 88 > /sys/class/gpio/export\n", "r"); + pclose(fp); + fp = popen("echo \"in\" > /sys/class/gpio/gpio88/direction\n", "r"); + pclose(fp); + fp = popen("chmod 0666 /sys/class/gpio/gpio88/value\n", "r"); + pclose(fp); + + fp = popen("echo 87 > /sys/class/gpio/export\n", "r"); + pclose(fp); + fp = popen("echo \"in\" > /sys/class/gpio/gpio87/direction\n", "r"); + pclose(fp); + fp = popen("chmod 0666 /sys/class/gpio/gpio87/value\n", "r"); + pclose(fp); + + if ( wiringPiISR (0, INT_EDGE_BOTH, &interruptB) < 0 ) { + printf ("Unable to setup ISR: %s\n", strerror (errno)); + return 1; + } + + if ( wiringPiISR (1, INT_EDGE_BOTH, &interruptA) < 0 ) { + printf ("Unable to setup ISR: %s\n", strerror (errno)); + return 1; + } +#endif +// } else { +// fprintf(stderr,"Unknown nodename: %s. Rotary Encoder not enabled.\n",unameData.nodename); +// return 1; +// } + + int rc=pthread_create(&rotary_encoder_thread_id, NULL, rotary_encoder_thread, NULL); + if(rc<0) { + fprintf(stderr,"pthread_create for rotary_encoder_thread failed %d\n",rc); + } + + + return 0; +} + +void gpio_close() { +// if(strcmp(unameData.nodename,"raspberrypi")==0) { +#ifdef raspberrypi + gpioTerminate(); +#endif +// } +// if(strcmp(unameData.nodename,"odroid")==0) { +#ifdef odroid + FILE *fp; + fp = popen("echo 97 > /sys/class/gpio/unexport\n", "r"); + pclose(fp); + fp = popen("echo 108 > /sys/class/gpio/unexport\n", "r"); + pclose(fp); +#endif +// } +} + +int vfo_encoder_get_pos() { + int pos=vfoEncoderPos; + + if(vfo_encoder_divisor>1) { + if(pos<0 && pos>-vfo_encoder_divisor) { + pos=0; + } else if(pos>0 && posfrequencyA=entry->frequencyA+(pos*step); + setFrequency(entry->frequencyA); + vfo_update(NULL); + } + free(data); + return 0; +} + +static int af_encoder_changed(void *data) { + int pos=*(int*)data; + if(pos!=0) { + if(function) { + // mic gain + double gain=mic_gain; + gain+=(double)pos/100.0; + if(gain<0.0) { + gain=0.0; + } else if(gain>4.0) { + gain=4.0; + } + set_mic_gain(gain); + } else { + // af gain + double gain=volume; + gain+=(double)pos/100.0; + if(gain<0.0) { + gain=0.0; + } else if(gain>1.0) { + gain=1.0; + } + set_af_gain(gain); + } + } + free(data); + return 0; +} + +static int rf_encoder_changed(void *data) { + int pos=*(int*)data; + if(pos!=0) { + if(function || tune) { + // tune drive + double d=getTuneDrive(); + d+=(double)pos/100.0; + if(d<0.0) { + d=0.0; + } else if(d>1.0) { + d=1.0; + } + set_tune(d); + } else { + // drive + double d=getDrive(); + d+=(double)pos/100.0; + if(d<0.0) { + d=0.0; + } else if(d>1.0) { + d=1.0; + } + set_drive(d); + } + } + free(data); + return 0; +} + +static int agc_encoder_changed(void *data) { + int pos=*(int*)data; + if(pos!=0) { + double gain=agc_gain; + gain+=(double)pos; + if(gain<0.0) { + gain=0.0; + } else if(gain>120.0) { + gain=120.0; + } + set_agc_gain(gain); + } + return 0; +} + +static int band_pressed(void *data) { + BAND* band; + BANDSTACK_ENTRY *entry; +fprintf(stderr,"band_pressed\n"); + int b=band_get_current(); + if(function) { + b--; + if(b<0) { + b=BANDS-1; + } + } else { + b++; + if(b>=BANDS) { + b=0; + } + } + band=band_set_current(b); + entry=bandstack_entry_get_current(); + + setFrequency(entry->frequencyA); + setMode(entry->mode); + FILTER* band_filters=filters[entry->mode]; + FILTER* band_filter=&band_filters[entry->filter]; + setFilter(band_filter->low,band_filter->high); + + band=band_get_current_band(); + set_alex_rx_antenna(band->alexRxAntenna); + set_alex_tx_antenna(band->alexTxAntenna); + set_alex_attenuation(band->alexAttenuation); + vfo_update(NULL); + + return 0; +} + +static int bandstack_pressed(void *data) { + BANDSTACK_ENTRY *entry; + fprintf(stderr,"bandstack_pressed\n"); + if(function) { + entry=bandstack_entry_previous(); + } else { + entry=bandstack_entry_next(); + } + setFrequency(entry->frequencyA); + setMode(entry->mode); + FILTER* band_filters=filters[entry->mode]; + FILTER* band_filter=&band_filters[entry->filter]; + setFilter(band_filter->low,band_filter->high); + vfo_update(NULL); + return 0; +} + +static int function_pressed(void *data) { +fprintf(stderr,"function_pressed\n"); + function=function==1?0:1; + vfo_update(NULL); + return 0; +} + +static int mox_pressed(void *data) { +fprintf(stderr,"mox_pressed\n"); + if(function) { + tune_cb((GtkWidget *)NULL, (gpointer)NULL); + } else { + mox_cb((GtkWidget *)NULL, (gpointer)NULL); + } + return 0; +} + +static int lock_pressed(void *data) { +fprintf(stderr,"lock_pressed\n"); + lock_cb((GtkWidget *)NULL, (gpointer)NULL); + return 0; +} + +static int mode_pressed(void *data) { + BAND* band; + BANDSTACK_ENTRY *entry; + +fprintf(stderr,"mode_pressed\n"); + band=band_get_current_band(); + entry=bandstack_entry_get_current(); + if(function) { + entry->mode--; + if(entry->mode<0) { + entry->mode=MODES-1; + } + } else { + entry->mode++; + if(entry->mode>=MODES) { + entry->mode=0; + } + } + setMode(entry->mode); + + FILTER* band_filters=filters[entry->mode]; + FILTER* band_filter=&band_filters[entry->filter]; + setFilter(band_filter->low,band_filter->high); + + vfo_update(NULL); + + return 0; +} + +static int filter_pressed(void *data) { + BAND* band; + BANDSTACK_ENTRY *entry; + +fprintf(stderr,"filter_pressed\n"); + band=band_get_current_band(); + entry=bandstack_entry_get_current(); + // note order of filter reversed (largest first) + if(function) { + entry->filter++; + if(entry->filter>=FILTERS) { + entry->filter=0; + } + } else { + entry->filter--; + if(entry->filter<0) { + entry->filter=FILTERS-1; + } + } + + FILTER* band_filters=filters[entry->mode]; + FILTER* band_filter=&band_filters[entry->filter]; + setFilter(band_filter->low,band_filter->high); + + vfo_update(NULL); + + return 0; +} + +static int noise_pressed(void *data) { +fprintf(stderr,"noise_pressed\n"); + if(function) { + if(nr) { + nr=0; + } else if(nb) { + nr=1; + nb=0; + } else if(anf) { + nb=1; + anf=0; + } else if(snb) { + snb=0; + anf=1; + } else { + snb=1; + } + } else { + if(nr) { + nr=0; + nb=1; + } else if(nb) { + nb=0; + anf=1; + } else if(anf) { + anf=0; + snb=1; + } else if(snb) { + snb=0; + } else { + nr=1; + } + } + SetRXAANRRun(CHANNEL_RX0, nr); + SetRXAEMNRRun(CHANNEL_RX0, nb); + SetRXAANFRun(CHANNEL_RX0, anf); + SetRXASNBARun(CHANNEL_RX0, snb); + vfo_update(NULL); + return 0; +} + +static int agc_pressed(void *data) { +fprintf(stderr,"agc_pressed\n"); + if(function) { + agc--; + if(agc<0) { + agc=3; + } + } else { + agc++; + if(agc>=4) { + agc=0; + } + } + SetRXAAGCMode(CHANNEL_RX0, agc); + vfo_update(NULL); + return 0; +} + +static void* rotary_encoder_thread(void *arg) { + int pos; + while(1) { + + int function_button=function_get_state(); + if(function_button!=previous_function_button) { + previous_function_button=function_button; + if(function_button) { + g_idle_add(function_pressed,(gpointer)NULL); + } + } + + pos=vfo_encoder_get_pos(); + if(pos!=0) { + int *p=malloc(sizeof(int)); + *p=pos; + g_idle_add(vfo_encoder_changed,(gpointer)p); + } + +/* + af_function=af_function_get_state(); + if(af_function!=previous_af_function) { + previous_af_function=af_function; + } +*/ + pos=af_encoder_get_pos(); + if(pos!=0) { + int *p=malloc(sizeof(int)); + *p=pos; + g_idle_add(af_encoder_changed,(gpointer)p); + } + +/* + rf_function=rf_function_get_state(); + if(rf_function!=previous_rf_function) { + previous_rf_function=rf_function; + } +*/ + pos=rf_encoder_get_pos(); + if(pos!=0) { + int *p=malloc(sizeof(int)); + *p=pos; + g_idle_add(rf_encoder_changed,(gpointer)p); + } + + pos=agc_encoder_get_pos(); + if(pos!=0) { + int *p=malloc(sizeof(int)); + *p=pos; + g_idle_add(agc_encoder_changed,(gpointer)p); + } + + + int band_button=band_get_state(); + if(band_button!=previous_band_button) { + previous_band_button=band_button; + if(band_button) { + g_idle_add(band_pressed,(gpointer)NULL); + } + } + + int bandstack_button=bandstack_get_state(); + if(bandstack_button!=previous_bandstack_button) { + previous_bandstack_button=bandstack_button; + if(bandstack_button) { + g_idle_add(bandstack_pressed,(gpointer)NULL); + } + } + + int mode_button=mode_get_state(); + if(mode_button!=previous_mode_button) { + previous_mode_button=mode_button; + if(mode_button) { + g_idle_add(mode_pressed,(gpointer)NULL); + } + } + + int filter_button=filter_get_state(); + if(filter_button!=previous_filter_button) { + previous_filter_button=filter_button; + if(filter_button) { + g_idle_add(filter_pressed,(gpointer)NULL); + } + } + + int noise_button=noise_get_state(); + if(noise_button!=previous_noise_button) { + previous_noise_button=noise_button; + if(noise_button) { + g_idle_add(noise_pressed,(gpointer)NULL); + } + } + + int agc_button=agc_get_state(); + if(agc_button!=previous_agc_button) { + previous_agc_button=agc_button; + if(agc_button) { + g_idle_add(agc_pressed,(gpointer)NULL); + } + } + + int mox_button=mox_get_state(); + if(mox_button!=previous_mox_button) { + previous_mox_button=mox_button; + if(mox_button) { + g_idle_add(mox_pressed,(gpointer)NULL); + } + } + + int lock_button=lock_get_state(); + if(lock_button!=previous_lock_button) { + previous_lock_button=lock_button; + if(lock_button) { + g_idle_add(lock_pressed,(gpointer)NULL); + } + } + +// if(strcmp(unameData.nodename,"raspberrypi")==0) { +#ifdef raspberrypi + gpioDelay(100000); // 10 per second +#endif +// } else if(strcmp(unameData.nodename,"odroid")==0) { +#ifdef odroid + usleep(100000); +#endif +// } + } +} diff --git a/gpio.h b/gpio.h new file mode 100644 index 0000000..569b616 --- /dev/null +++ b/gpio.h @@ -0,0 +1,71 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +extern int ENABLE_VFO_ENCODER; +extern int ENABLE_VFO_PULLUP; +extern int VFO_ENCODER_A; +extern int VFO_ENCODER_B; +extern int VFO_ENCODER_A_PIN; +extern int VFO_ENCODER_B_PIN; +extern int ENABLE_AF_ENCODER; +extern int ENABLE_AF_PULLUP; +extern int AF_ENCODER_A; +extern int AF_ENCODER_B; +extern int ENABLE_RF_ENCODER; +extern int ENABLE_RF_PULLUP; +extern int RF_ENCODER_A; +extern int RF_ENCODER_B; +extern int ENABLE_AGC_ENCODER; +extern int ENABLE_AGC_PULLUP; +extern int AGC_ENCODER_A; +extern int AGC_ENCODER_B; +extern int ENABLE_BAND_BUTTON; +extern int BAND_BUTTON; +extern int ENABLE_BANDSTACK_BUTTON; +extern int BANDSTACK_BUTTON; +extern int ENABLE_MODE_BUTTON; +extern int MODE_BUTTON; +extern int ENABLE_FILTER_BUTTON; +extern int FILTER_BUTTON; +extern int ENABLE_NOISE_BUTTON; +extern int NOISE_BUTTON; +extern int ENABLE_AGC_BUTTON; +extern int AGC_BUTTON; +extern int ENABLE_MOX_BUTTON; +extern int MOX_BUTTON; +extern int ENABLE_FUNCTION_BUTTON; +extern int FUNCTION_BUTTON; + +extern int function; +void gpio_restore_state(); +void gpio_save_state(); +int gpio_init(); +void gpio_close(); +int vfo_encoder_get_pos(); +int af_encoder_get_pos(); +int af_function_get_state(); +int rf_encoder_get_pos(); +int rf_function_get_state(); +int function_get_state(); +int band_get_state(); +int mode_get_state(); +int filter_get_state(); +int noise_get_state(); +int mox_get_state(); + diff --git a/main.c b/main.c index 814e9f5..ab7183b 100644 --- a/main.c +++ b/main.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #include #include @@ -11,24 +30,32 @@ #include "main.h" #include "channel.h" #include "discovered.h" +#include "gpio.h" +#include "old_discovery.h" #include "new_discovery.h" #include "new_protocol.h" #include "wdsp.h" #include "vfo.h" +#include "menu.h" #include "meter.h" #include "panadapter.h" #include "splash.h" #include "waterfall.h" #include "toolbar.h" #include "radio.h" - -#define VFO_HEIGHT (display_height/8) -#define VFO_WIDTH ((display_width/4)*3) -#define METER_HEIGHT (display_height/8) -#define METER_WIDTH (display_width/4) -#define PANADAPTER_HEIGHT (display_height/4) -#define TOOLBAR_HEIGHT (display_height/4) -#define WATERFALL_HEIGHT (display_height-(VFO_HEIGHT+PANADAPTER_HEIGHT+TOOLBAR_HEIGHT)) +#include "wdsp_init.h" +#include "version.h" + +#define VFO_HEIGHT ((display_height/32)*4) +#define VFO_WIDTH ((display_width/32)*16) +#define MENU_HEIGHT VFO_HEIGHT +#define MENU_WIDTH ((display_width/32)*3) +#define METER_HEIGHT VFO_HEIGHT +#define METER_WIDTH ((display_width/32)*13) +#define PANADAPTER_HEIGHT ((display_height/32)*8) +#define SLIDERS_HEIGHT ((display_height/32)*6) +#define TOOLBAR_HEIGHT ((display_height/32)*2) +#define WATERFALL_HEIGHT (display_height-(VFO_HEIGHT+PANADAPTER_HEIGHT+SLIDERS_HEIGHT+TOOLBAR_HEIGHT)) struct utsname unameData; @@ -46,74 +73,130 @@ static int start=0; static GtkWidget *discovery_dialog; +static sem_t wisdom_sem; + +static GdkCursor *cursor_arrow; +static GdkCursor *cursor_watch; + gint update(gpointer data) { int result; - GetPixels(isTransmitting()==0?CHANNEL_RX0:CHANNEL_TX,samples,&result); + double fwd; + double rev; + double exciter; + + GetPixels(isTransmitting()==0?CHANNEL_RX0:CHANNEL_TX,0,samples,&result); if(result==1) { - panadapter_update(samples,isTransmitting()); + if(display_panadapter) { + panadapter_update(samples,isTransmitting()); + } if(!isTransmitting()) { - waterfall_update(samples); + if(display_waterfall) { + waterfall_update(samples); + } } } if(!isTransmitting()) { float m=GetRXAMeter(CHANNEL_RX0, 1/*WDSP.S_AV*/); - meter_update(SMETER,(double)m,0.0); + meter_update(SMETER,(double)m,0.0,0.0); } else { DISCOVERED *d=&discovered[selected_device]; - double constant1=5.0; - double constant2=0.108; - - switch(d->device) { - case NEW_DEVICE_ATLAS: - constant1=3.3; - constant2=0.09; - break; - case NEW_DEVICE_HERMES: - constant1=3.3; - constant2=0.09; - break; - case NEW_DEVICE_HERMES2: - constant1=3.3; - constant2=0.095; - break; - case NEW_DEVICE_ANGELIA: - constant1=3.3; - constant2=0.095; - break; - case NEW_DEVICE_ORION: - constant1=5.0; - constant2=0.108; - break; - case NEW_DEVICE_ANAN_10E: - constant1=3.3; - constant2=0.09; - break; - case NEW_DEVICE_HERMES_LITE: - constant1=3.3; - constant2=0.09; - break; - } + double constant1=3.3; + double constant2=0.09; + + if(d->protocol==ORIGINAL_PROTOCOL) { + switch(d->device) { + case DEVICE_METIS: + break; + case DEVICE_HERMES: + //constant2=0.095; HERMES 2 + break; + case DEVICE_ANGELIA: + break; + case DEVICE_ORION: + constant1=5.0; + constant2=0.108; + break; + case DEVICE_HERMES_LITE: + break; + } + + int power=alex_forward_power; + if(power==0) { + power=exciter_power; + } + double v1; + v1=((double)power/4095.0)*constant1; + fwd=(v1*v1)/constant2; + + power=exciter_power; + v1=((double)power/4095.0)*constant1; + exciter=(v1*v1)/constant2; + + rev=0.0; + if(alex_forward_power!=0) { + power=alex_reverse_power; + v1=((double)power/4095.0)*constant1; + rev=(v1*v1)/constant2; + } + + } else { + switch(d->device) { + case NEW_DEVICE_ATLAS: + constant1=3.3; + constant2=0.09; + break; + case NEW_DEVICE_HERMES: + constant1=3.3; + constant2=0.09; + break; + case NEW_DEVICE_HERMES2: + constant1=3.3; + constant2=0.095; + break; + case NEW_DEVICE_ANGELIA: + constant1=3.3; + constant2=0.095; + break; + case NEW_DEVICE_ORION: + constant1=5.0; + constant2=0.108; + break; + case NEW_DEVICE_ANAN_10E: + constant1=3.3; + constant2=0.09; + break; + case NEW_DEVICE_HERMES_LITE: + constant1=3.3; + constant2=0.09; + break; + } - int power=alex_forward_power; - if(power==0) { + int power=alex_forward_power; + if(power==0) { + power=exciter_power; + } + double v1; + v1=((double)power/4095.0)*constant1; + fwd=(v1*v1)/constant2; + power=exciter_power; - } - double v1; - double fwd; - double rev; - v1=((double)power/4095.0)*constant1; - fwd=(v1*v1)/constant2; - - rev=0.0; - if(alex_forward_power!=0) { - power=alex_reverse_power; v1=((double)power/4095.0)*constant1; - rev=(v1*v1)/constant2; + exciter=(v1*v1)/constant2; + + rev=0.0; + if(alex_forward_power!=0) { + power=alex_reverse_power; + v1=((double)power/4095.0)*constant1; + rev=(v1*v1)/constant2; + } } - meter_update(POWER,fwd,rev); + +//fprintf(stderr,"drive=%d tune_drive=%d alex_forward_power=%d alex_reverse_power=%d exciter_power=%d fwd=%f rev=%f exciter=%f\n", +// drive, tune_drive, alex_forward_power, alex_reverse_power, exciter_power, fwd, rev, exciter); + meter_update(POWER,fwd,rev,exciter); } return TRUE; @@ -121,7 +204,6 @@ gint update(gpointer data) { static gint save_cb(gpointer data) { radioSaveState(); - saveProperties(property_path); return TRUE; } @@ -132,39 +214,480 @@ fprintf(stderr,"start_cb\n"); gtk_widget_destroy(discovery_dialog); } +static void sample_rate_cb(GtkWidget *widget, gpointer data) { + sample_rate=(int)data; +} + +static void display_panadapter_cb(GtkWidget *widget, gpointer data) { + display_panadapter=display_panadapter==1?0:1; +} + +static void display_waterfall_cb(GtkWidget *widget, gpointer data) { + display_waterfall=display_waterfall==1?0:1; +} + +static void display_sliders_cb(GtkWidget *widget, gpointer data) { + display_sliders=display_sliders==1?0:1; +} + +static void display_toolbar_cb(GtkWidget *widget, gpointer data) { + display_toolbar=display_toolbar==1?0:1; +} + +static void configure_gpio() { + gpio_restore_state(); + + GtkWidget *dialog=gtk_dialog_new_with_buttons("Configure GPIO",GTK_WINDOW(splash_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkWidget *grid=gtk_grid_new(); + gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE); + gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE); + + + GtkWidget *b_enable_vfo_encoder=gtk_check_button_new_with_label("Enable VFO"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_vfo_encoder), ENABLE_VFO_ENCODER); + gtk_widget_show(b_enable_vfo_encoder); + gtk_grid_attach(GTK_GRID(grid),b_enable_vfo_encoder,0,0,1,1); + + GtkWidget *vfo_a_label=gtk_label_new("GPIO A:"); + gtk_widget_show(vfo_a_label); + gtk_grid_attach(GTK_GRID(grid),vfo_a_label,1,0,1,1); + + GtkWidget *vfo_a=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(vfo_a),VFO_ENCODER_A); + gtk_widget_show(vfo_a); + gtk_grid_attach(GTK_GRID(grid),vfo_a,2,0,1,1); + + GtkWidget *vfo_b_label=gtk_label_new("GPIO B:"); + gtk_widget_show(vfo_b_label); + gtk_grid_attach(GTK_GRID(grid),vfo_b_label,3,0,1,1); + + GtkWidget *vfo_b=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(vfo_b),VFO_ENCODER_B); + gtk_widget_show(vfo_b); + gtk_grid_attach(GTK_GRID(grid),vfo_b,4,0,1,1); + + GtkWidget *b_enable_vfo_pullup=gtk_check_button_new_with_label("Enable Pull-up"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_vfo_pullup), ENABLE_VFO_PULLUP); + gtk_widget_show(b_enable_vfo_pullup); + gtk_grid_attach(GTK_GRID(grid),b_enable_vfo_pullup,5,0,1,1); + + + + GtkWidget *b_enable_af_encoder=gtk_check_button_new_with_label("Enable AF"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_af_encoder), ENABLE_AF_ENCODER); + gtk_widget_show(b_enable_af_encoder); + gtk_grid_attach(GTK_GRID(grid),b_enable_af_encoder,0,1,1,1); + + GtkWidget *af_a_label=gtk_label_new("GPIO A:"); + gtk_widget_show(af_a_label); + gtk_grid_attach(GTK_GRID(grid),af_a_label,1,1,1,1); + + GtkWidget *af_a=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(af_a),AF_ENCODER_A); + gtk_widget_show(af_a); + gtk_grid_attach(GTK_GRID(grid),af_a,2,1,1,1); + + GtkWidget *af_b_label=gtk_label_new("GPIO B:"); + gtk_widget_show(af_b_label); + gtk_grid_attach(GTK_GRID(grid),af_b_label,3,1,1,1); + + GtkWidget *af_b=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(af_b),AF_ENCODER_B); + gtk_widget_show(af_b); + gtk_grid_attach(GTK_GRID(grid),af_b,4,1,1,1); + + GtkWidget *b_enable_af_pullup=gtk_check_button_new_with_label("Enable Pull-up"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_af_pullup), ENABLE_AF_PULLUP); + gtk_widget_show(b_enable_af_pullup); + gtk_grid_attach(GTK_GRID(grid),b_enable_af_pullup,5,1,1,1); + + + + GtkWidget *b_enable_rf_encoder=gtk_check_button_new_with_label("Enable RF"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_rf_encoder), ENABLE_RF_ENCODER); + gtk_widget_show(b_enable_rf_encoder); + gtk_grid_attach(GTK_GRID(grid),b_enable_rf_encoder,0,2,1,1); + + GtkWidget *rf_a_label=gtk_label_new("GPIO A:"); + gtk_widget_show(rf_a_label); + gtk_grid_attach(GTK_GRID(grid),rf_a_label,1,2,1,1); + + GtkWidget *rf_a=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(rf_a),RF_ENCODER_A); + gtk_widget_show(rf_a); + gtk_grid_attach(GTK_GRID(grid),rf_a,2,2,1,1); + + GtkWidget *rf_b_label=gtk_label_new("GPIO B:"); + gtk_widget_show(rf_b_label); + gtk_grid_attach(GTK_GRID(grid),rf_b_label,3,2,1,1); + + GtkWidget *rf_b=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(rf_b),RF_ENCODER_B); + gtk_widget_show(rf_b); + gtk_grid_attach(GTK_GRID(grid),rf_b,4,2,1,1); + + GtkWidget *b_enable_rf_pullup=gtk_check_button_new_with_label("Enable Pull-up"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_rf_pullup), ENABLE_RF_PULLUP); + gtk_widget_show(b_enable_rf_pullup); + gtk_grid_attach(GTK_GRID(grid),b_enable_rf_pullup,5,2,1,1); + + + + GtkWidget *b_enable_agc_encoder=gtk_check_button_new_with_label("Enable AGC"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_agc_encoder), ENABLE_AGC_ENCODER); + gtk_widget_show(b_enable_agc_encoder); + gtk_grid_attach(GTK_GRID(grid),b_enable_agc_encoder,0,3,1,1); + + GtkWidget *agc_a_label=gtk_label_new("GPIO A:"); + gtk_widget_show(agc_a_label); + gtk_grid_attach(GTK_GRID(grid),agc_a_label,1,3,1,1); + + GtkWidget *agc_a=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(agc_a),AGC_ENCODER_A); + gtk_widget_show(agc_a); + gtk_grid_attach(GTK_GRID(grid),agc_a,2,3,1,1); + + GtkWidget *agc_b_label=gtk_label_new("GPIO B:"); + gtk_widget_show(agc_b_label); + gtk_grid_attach(GTK_GRID(grid),agc_b_label,3,3,1,1); + + GtkWidget *agc_b=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(agc_b),AGC_ENCODER_B); + gtk_widget_show(agc_b); + gtk_grid_attach(GTK_GRID(grid),agc_b,4,3,1,1); + + GtkWidget *b_enable_agc_pullup=gtk_check_button_new_with_label("Enable Pull-up"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_agc_pullup), ENABLE_AGC_PULLUP); + gtk_widget_show(b_enable_agc_pullup); + gtk_grid_attach(GTK_GRID(grid),b_enable_agc_pullup,5,3,1,1); + + + + GtkWidget *b_enable_band=gtk_check_button_new_with_label("Enable Band"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_band), ENABLE_BAND_BUTTON); + gtk_widget_show(b_enable_band); + gtk_grid_attach(GTK_GRID(grid),b_enable_band,0,4,1,1); + + GtkWidget *band_label=gtk_label_new("GPIO:"); + gtk_widget_show(band_label); + gtk_grid_attach(GTK_GRID(grid),band_label,1,4,1,1); + + GtkWidget *band=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(band),BAND_BUTTON); + gtk_widget_show(band); + gtk_grid_attach(GTK_GRID(grid),band,2,4,1,1); + + + GtkWidget *b_enable_mode=gtk_check_button_new_with_label("Enable Mode"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_mode), ENABLE_MODE_BUTTON); + gtk_widget_show(b_enable_mode); + gtk_grid_attach(GTK_GRID(grid),b_enable_mode,0,5,1,1); + + GtkWidget *mode_label=gtk_label_new("GPIO:"); + gtk_widget_show(mode_label); + gtk_grid_attach(GTK_GRID(grid),mode_label,1,5,1,1); + + GtkWidget *mode=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(mode),MODE_BUTTON); + gtk_widget_show(mode); + gtk_grid_attach(GTK_GRID(grid),mode,2,5,1,1); + + + GtkWidget *b_enable_filter=gtk_check_button_new_with_label("Enable Filter"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_filter), ENABLE_FILTER_BUTTON); + gtk_widget_show(b_enable_filter); + gtk_grid_attach(GTK_GRID(grid),b_enable_filter,0,6,1,1); + + GtkWidget *filter_label=gtk_label_new("GPIO:"); + gtk_widget_show(filter_label); + gtk_grid_attach(GTK_GRID(grid),filter_label,1,6,1,1); + + GtkWidget *filter=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(filter),FILTER_BUTTON); + gtk_widget_show(filter); + gtk_grid_attach(GTK_GRID(grid),filter,2,6,1,1); + + + GtkWidget *b_enable_noise=gtk_check_button_new_with_label("Enable Noise"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_noise), ENABLE_NOISE_BUTTON); + gtk_widget_show(b_enable_noise); + gtk_grid_attach(GTK_GRID(grid),b_enable_noise,0,7,1,1); + + GtkWidget *noise_label=gtk_label_new("GPIO:"); + gtk_widget_show(noise_label); + gtk_grid_attach(GTK_GRID(grid),noise_label,1,7,1,1); + + GtkWidget *noise=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(noise),NOISE_BUTTON); + gtk_widget_show(noise); + gtk_grid_attach(GTK_GRID(grid),noise,2,7,1,1); + + + GtkWidget *b_enable_agc=gtk_check_button_new_with_label("Enable AGC"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_agc), ENABLE_AGC_BUTTON); + gtk_widget_show(b_enable_agc); + gtk_grid_attach(GTK_GRID(grid),b_enable_agc,0,8,1,1); + + GtkWidget *agc_label=gtk_label_new("GPIO:"); + gtk_widget_show(agc_label); + gtk_grid_attach(GTK_GRID(grid),agc_label,1,8,1,1); + + GtkWidget *agc=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(agc),AGC_BUTTON); + gtk_widget_show(agc); + gtk_grid_attach(GTK_GRID(grid),agc,2,8,1,1); + + + GtkWidget *b_enable_mox=gtk_check_button_new_with_label("Enable MOX"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_mox), ENABLE_MOX_BUTTON); + gtk_widget_show(b_enable_mox); + gtk_grid_attach(GTK_GRID(grid),b_enable_mox,0,9,1,1); + + GtkWidget *mox_label=gtk_label_new("GPIO:"); + gtk_widget_show(mox_label); + gtk_grid_attach(GTK_GRID(grid),mox_label,1,9,1,1); + + GtkWidget *mox=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(mox),MOX_BUTTON); + gtk_widget_show(mox); + gtk_grid_attach(GTK_GRID(grid),mox,2,9,1,1); + + + GtkWidget *b_enable_function=gtk_check_button_new_with_label("Enable Function"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_function), ENABLE_FUNCTION_BUTTON); + gtk_widget_show(b_enable_function); + gtk_grid_attach(GTK_GRID(grid),b_enable_function,0,10,1,1); + + GtkWidget *function_label=gtk_label_new("GPIO:"); + gtk_widget_show(function_label); + gtk_grid_attach(GTK_GRID(grid),function_label,1,10,1,1); + + GtkWidget *function=gtk_spin_button_new_with_range (0.0,100.0,1.0); + gtk_spin_button_set_value (GTK_SPIN_BUTTON(function),FUNCTION_BUTTON); + gtk_widget_show(function); + gtk_grid_attach(GTK_GRID(grid),function,2,10,1,1); + + + + gtk_container_add(GTK_CONTAINER(content),grid); + + gtk_container_add(GTK_CONTAINER(content),grid); + + GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK); + //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20")); + gtk_widget_show_all(dialog); + +/* + g_signal_connect_swapped (dialog, + "response", + G_CALLBACK (gtk_widget_destroy), + dialog); +*/ + + int result=gtk_dialog_run(GTK_DIALOG(dialog)); + + ENABLE_VFO_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_vfo_encoder))?1:0; + VFO_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(vfo_a)); + VFO_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(vfo_b)); + ENABLE_AF_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_af_encoder))?1:0; + AF_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(af_a)); + AF_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(af_b)); + ENABLE_RF_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_rf_encoder))?1:0; + RF_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rf_a)); + RF_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(rf_b)); + ENABLE_AGC_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_agc_encoder))?1:0; + AGC_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(agc_a)); + AGC_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(agc_b)); + ENABLE_BAND_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_band))?1:0; + BAND_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(band)); + ENABLE_MODE_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_mode))?1:0; + MODE_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mode)); + ENABLE_FILTER_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_filter))?1:0; + FILTER_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(filter)); + ENABLE_NOISE_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_noise))?1:0; + NOISE_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(noise)); + ENABLE_AGC_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_agc))?1:0; + AGC_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(agc)); + ENABLE_MOX_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_mox))?1:0; + MOX_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mox)); + ENABLE_FUNCTION_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_function))?1:0; + FUNCTION_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(function)); + + gtk_widget_destroy(dialog); + + gpio_save_state(); +} + static void configure_cb(GtkWidget *widget, gpointer data) { + DISCOVERED* d; + d=&discovered[(int)data]; + sprintf(property_path,"%02X-%02X-%02X-%02X-%02X-%02X.props", + d->mac_address[0], + d->mac_address[1], + d->mac_address[2], + d->mac_address[3], + d->mac_address[4], + d->mac_address[5]); + radioRestoreState(); + + GtkWidget *dialog=gtk_dialog_new_with_buttons("Configure",GTK_WINDOW(splash_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkWidget *grid=gtk_grid_new(); + gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE); + gtk_grid_set_row_homogeneous(GTK_GRID(grid),FALSE); + + GtkWidget *sample_rate_label=gtk_label_new("Sample Rate:"); + //gtk_widget_override_font(sample_rate_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(sample_rate_label); + gtk_grid_attach(GTK_GRID(grid),sample_rate_label,0,0,1,1); + + GtkWidget *sample_rate_48=gtk_radio_button_new_with_label(NULL,"48000"); + //gtk_widget_override_font(sample_rate_48, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_48), sample_rate==48000); + gtk_widget_show(sample_rate_48); + gtk_grid_attach(GTK_GRID(grid),sample_rate_48,0,1,1,1); + g_signal_connect(sample_rate_48,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)48000); + + GtkWidget *sample_rate_96=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_48),"96000"); + //gtk_widget_override_font(sample_rate_96, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_96), sample_rate==96000); + gtk_widget_show(sample_rate_96); + gtk_grid_attach(GTK_GRID(grid),sample_rate_96,0,2,1,1); + g_signal_connect(sample_rate_96,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)96000); + + GtkWidget *sample_rate_192=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_96),"192000"); + //gtk_widget_override_font(sample_rate_192, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_192), sample_rate==192000); + gtk_widget_show(sample_rate_192); + gtk_grid_attach(GTK_GRID(grid),sample_rate_192,0,3,1,1); + g_signal_connect(sample_rate_192,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)192000); + + GtkWidget *sample_rate_384=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_192),"384000"); + //gtk_widget_override_font(sample_rate_384, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_384), sample_rate==384000); + gtk_widget_show(sample_rate_384); + gtk_grid_attach(GTK_GRID(grid),sample_rate_384,0,4,1,1); + g_signal_connect(sample_rate_384,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)384000); + + + GtkWidget *display_label=gtk_label_new("Display:"); + //gtk_widget_override_font(display_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(display_label); + gtk_grid_attach(GTK_GRID(grid),display_label,0,5,1,1); + + GtkWidget *b_display_panadapter=gtk_check_button_new_with_label("Display Panadapter"); + //gtk_widget_override_font(b_display_panadapter, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_panadapter), display_panadapter); + gtk_widget_show(b_display_panadapter); + gtk_grid_attach(GTK_GRID(grid),b_display_panadapter,0,6,1,1); + g_signal_connect(b_display_panadapter,"toggled",G_CALLBACK(display_panadapter_cb),(gpointer *)NULL); + + GtkWidget *b_display_waterfall=gtk_check_button_new_with_label("Display Waterfall"); + //gtk_widget_override_font(b_display_waterfall, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_waterfall), display_waterfall); + gtk_widget_show(b_display_waterfall); + gtk_grid_attach(GTK_GRID(grid),b_display_waterfall,0,7,1,1); + g_signal_connect(b_display_waterfall,"toggled",G_CALLBACK(display_waterfall_cb),(gpointer *)NULL); + + GtkWidget *b_display_sliders=gtk_check_button_new_with_label("Display Sliders"); + //gtk_widget_override_font(b_display_sliders, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_sliders), display_sliders); + gtk_widget_show(b_display_sliders); + gtk_grid_attach(GTK_GRID(grid),b_display_sliders,0,8,1,1); + g_signal_connect(b_display_sliders,"toggled",G_CALLBACK(display_sliders_cb),(gpointer *)NULL); + + GtkWidget *b_display_toolbar=gtk_check_button_new_with_label("Display Toolbar"); + //gtk_widget_override_font(b_display_toolbar, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_toolbar), display_toolbar); + gtk_widget_show(b_display_toolbar); + gtk_grid_attach(GTK_GRID(grid),b_display_toolbar,0,9,1,1); + g_signal_connect(b_display_toolbar,"toggled",G_CALLBACK(display_toolbar_cb),(gpointer *)NULL); + + gtk_container_add(GTK_CONTAINER(content),grid); + + GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK); + //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20")); + gtk_widget_show_all(dialog); + + g_signal_connect_swapped (dialog, + "response", + G_CALLBACK (gtk_widget_destroy), + dialog); + + int result=gtk_dialog_run(GTK_DIALOG(dialog)); + + radioSaveState(); + } +static pthread_t wisdom_thread_id; + +static void* wisdom_thread(void *arg) { + splash_status("Creating FFTW Wisdom file ..."); + WDSPwisdom ((char *)arg); + sem_post(&wisdom_sem); +} gint init(void* arg) { GtkWidget *window; GtkWidget *grid; + GtkWidget *fixed; + gint x; + gint y; GtkWidget *vfo; + GtkWidget *menu; GtkWidget *meter; GtkWidget *panadapter; GtkWidget *waterfall; + GtkWidget *sliders; GtkWidget *toolbar; DISCOVERED* d; + char *res; char wisdom_directory[1024]; char wisdom_file[1024]; fprintf(stderr,"init\n"); + cursor_arrow=gdk_cursor_new(GDK_ARROW); + cursor_watch=gdk_cursor_new(GDK_WATCH); + + GdkWindow *gdk_splash_window = gtk_widget_get_window(splash_window); + gdk_window_set_cursor(gdk_splash_window,cursor_watch); + + init_radio(); + // check if wisdom file exists - getcwd(wisdom_directory, sizeof(wisdom_directory)); + res=getcwd(wisdom_directory, sizeof(wisdom_directory)); strcpy(&wisdom_directory[strlen(wisdom_directory)],"/"); strcpy(wisdom_file,wisdom_directory); strcpy(&wisdom_file[strlen(wisdom_file)],"wdspWisdom"); + splash_status("Checking FFTW Wisdom file ..."); if(access(wisdom_file,F_OK)<0) { - WDSPwisdom (wisdom_directory); + int rc=sem_init(&wisdom_sem, 0, 0); + rc=pthread_create(&wisdom_thread_id, NULL, wisdom_thread, (void *)wisdom_directory); + while(sem_trywait(&wisdom_sem)<0) { + splash_status(wisdom_get_status()); + while (gtk_events_pending ()) + gtk_main_iteration (); + usleep(100000); // 100ms + } } while(!start) { + gdk_window_set_cursor(gdk_splash_window,cursor_watch); + selected_device=0; + devices=0; + splash_status("Old Protocol ... Discovering Devices"); + old_discovery(); + splash_status("New Protocol ... Discovering Devices"); new_discovery(); + splash_status("Discovery"); if(devices==0) { + gdk_window_set_cursor(gdk_splash_window,cursor_arrow); fprintf(stderr,"No devices found!\n"); GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT; discovery_dialog = gtk_message_dialog_new (GTK_WINDOW(splash_window), @@ -172,27 +695,30 @@ gint init(void* arg) { GTK_MESSAGE_ERROR, GTK_BUTTONS_OK_CANCEL, "No devices found! Retry Discovery?"); + gtk_widget_override_font(discovery_dialog, pango_font_description_from_string("Arial 18")); gint result=gtk_dialog_run (GTK_DIALOG (discovery_dialog)); + gtk_widget_destroy(discovery_dialog); if(result==GTK_RESPONSE_CANCEL) { _exit(0); } - } else if(devices==1) { - selected_device=0; - start=1; } else { fprintf(stderr,"%s: found %d devices.\n", (char *)arg, devices); + gdk_window_set_cursor(gdk_splash_window,cursor_arrow); GtkDialogFlags flags=GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT; discovery_dialog = gtk_dialog_new_with_buttons ("Discovered", GTK_WINDOW(splash_window), flags, + "Configure GPIO", + GTK_RESPONSE_YES, "Discover", GTK_RESPONSE_REJECT, + "Exit", + GTK_RESPONSE_CLOSE, NULL); + gtk_widget_override_font(discovery_dialog, pango_font_description_from_string("Arial 18")); GtkWidget *content; -// g_signal_connect_swapped(discovery_dialog,"response",G_CALLBACK(gtk_widget_destroy),discovery_dialog); - content=gtk_dialog_get_content_area(GTK_DIALOG(discovery_dialog)); GtkWidget *grid=gtk_grid_new(); @@ -203,8 +729,11 @@ gint init(void* arg) { char text[128]; for(i=0;iname, + d->protocol==ORIGINAL_PROTOCOL?"old":"new", + d->protocol==ORIGINAL_PROTOCOL?d->software_version/10:d->software_version/100, + d->protocol==ORIGINAL_PROTOCOL?d->software_version%10:d->software_version%100, inet_ntoa(d->address.sin_addr), d->mac_address[0], d->mac_address[1], @@ -215,27 +744,46 @@ gint init(void* arg) { d->interface_name); GtkWidget *label=gtk_label_new(text); + gtk_widget_override_font(label, pango_font_description_from_string("Arial 12")); gtk_widget_show(label); - gtk_grid_attach(GTK_GRID(grid),label,0,i,3,1); + gtk_grid_attach(GTK_GRID(grid),label,0,i,4,1); GtkWidget *start_button=gtk_button_new_with_label("Start"); + gtk_widget_override_font(start_button, pango_font_description_from_string("Arial 18")); gtk_widget_show(start_button); - gtk_grid_attach(GTK_GRID(grid),start_button,3,i,1,1); + gtk_grid_attach(GTK_GRID(grid),start_button,4,i,1,1); g_signal_connect(start_button,"pressed",G_CALLBACK(start_cb),(gpointer *)i); GtkWidget *configure_button=gtk_button_new_with_label("Configure"); + gtk_widget_override_font(configure_button, pango_font_description_from_string("Arial 18")); gtk_widget_show(configure_button); - gtk_grid_attach(GTK_GRID(grid),configure_button,4,i,1,1); + gtk_grid_attach(GTK_GRID(grid),configure_button,5,i,1,1); g_signal_connect(configure_button,"pressed",G_CALLBACK(configure_cb),(gpointer *)i); } gtk_container_add (GTK_CONTAINER (content), grid); gtk_widget_show_all(discovery_dialog); gint result=gtk_dialog_run(GTK_DIALOG(discovery_dialog)); + + if(result==GTK_RESPONSE_CLOSE) { + _exit(0); + } + + if(result==GTK_RESPONSE_YES) { + gtk_widget_destroy(discovery_dialog); + configure_gpio(); + } } } + gdk_window_set_cursor(gdk_splash_window,cursor_watch); + + splash_status("Initializing wdsp ..."); + d=&discovered[selected_device]; + protocol=d->protocol; + device=d->device; + sprintf(property_path,"%02X-%02X-%02X-%02X-%02X-%02X.props", d->mac_address[0], d->mac_address[1], @@ -244,39 +792,128 @@ gint init(void* arg) { d->mac_address[4], d->mac_address[5]); - loadProperties(property_path); radioRestoreState(); samples=malloc(display_width*sizeof(float)); - //selected_device=0; - setSampleRate(48000); - new_protocol_init(0,display_width); + //splash_status("Initializing wdsp ..."); + wdsp_init(0,display_width,d->protocol); - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_title (GTK_WINDOW (window), "pihpsdr"); + if(d->protocol==ORIGINAL_PROTOCOL) { + splash_status("Initializing old protocol ..."); + old_protocol_init(0,display_width); + } else { + splash_status("Initializing new protocol ..."); + new_protocol_init(0,display_width); + } - //g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL); + splash_status("Initializing GPIO ..."); + gpio_init(); + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (window), "pihpsdr"); gtk_container_set_border_width (GTK_CONTAINER (window), 0); +#ifdef GRID_LAYOUT grid = gtk_grid_new(); + gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE); + gtk_grid_set_row_homogeneous(GTK_GRID(grid),FALSE); gtk_container_add(GTK_CONTAINER(window), grid); +#else + fixed=gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(window), fixed); + y=0; +#endif +fprintf(stderr,"vfo_height=%d\n",VFO_HEIGHT); vfo = vfo_init(VFO_WIDTH,VFO_HEIGHT,window); - gtk_grid_attach(GTK_GRID(grid), vfo, 0, 0, 3, 1); - +#ifdef GRID_LAYOUT + gtk_grid_attach(GTK_GRID(grid), vfo, 0, y, 16, 1); +#else + gtk_fixed_put(GTK_FIXED(fixed),vfo,0,0); +#endif + + +fprintf(stderr,"menu_height=%d\n",MENU_HEIGHT); + menu = menu_init(MENU_WIDTH,MENU_HEIGHT,window); +#ifdef GRID_LAYOUT + gtk_grid_attach(GTK_GRID(grid), menu, 16, 0, 1, 1); +#else + gtk_fixed_put(GTK_FIXED(fixed),menu,VFO_WIDTH,y); +#endif + +fprintf(stderr,"meter_height=%d\n",METER_HEIGHT); meter = meter_init(METER_WIDTH,METER_HEIGHT); - gtk_grid_attach(GTK_GRID(grid), meter, 3, 0, 1, 1); +#ifdef GRID_LAYOUT + gtk_grid_attach(GTK_GRID(grid), meter, 17, 0, 15, 1); +#else + gtk_fixed_put(GTK_FIXED(fixed),meter,VFO_WIDTH+MENU_WIDTH,y); + y+=VFO_HEIGHT; +#endif + + if(display_panadapter) { + int height=PANADAPTER_HEIGHT; + if(!display_waterfall) { + height+=WATERFALL_HEIGHT; + if(!display_sliders) { + height+=SLIDERS_HEIGHT; + } + if(!display_toolbar) { + height+=TOOLBAR_HEIGHT; + } + } +fprintf(stderr,"panadapter_height=%d\n",height); + panadapter = panadapter_init(display_width,height); +#ifdef GRID_LAYOUT + gtk_grid_attach(GTK_GRID(grid), panadapter, 0, 1, 32, 1); +#else + gtk_fixed_put(GTK_FIXED(fixed),panadapter,0,VFO_HEIGHT); + y+=height; +#endif + } - panadapter = panadapter_init(display_width,PANADAPTER_HEIGHT); - gtk_grid_attach(GTK_GRID(grid), panadapter, 0, 1, 4, 1); + if(display_waterfall) { + int height=WATERFALL_HEIGHT; + if(!display_panadapter) { + height+=PANADAPTER_HEIGHT; + } + if(!display_sliders) { + height+=SLIDERS_HEIGHT; + } + if(!display_toolbar) { + height+=TOOLBAR_HEIGHT; + } +fprintf(stderr,"waterfall_height=%d\n",height); + waterfall = waterfall_init(display_width,height); +#ifdef GRID_LAYOUT + gtk_grid_attach(GTK_GRID(grid), waterfall, 0, 2, 32, 1); +#else + gtk_fixed_put(GTK_FIXED(fixed),waterfall,0,y); + y+=height; +#endif + } - waterfall = waterfall_init(display_width,WATERFALL_HEIGHT); - gtk_grid_attach(GTK_GRID(grid), waterfall, 0, 2, 4, 1); + if(display_sliders) { +fprintf(stderr,"sliders_height=%d\n",SLIDERS_HEIGHT); + sliders = sliders_init(display_width,SLIDERS_HEIGHT,window); +#ifdef GRID_LAYOUT + gtk_grid_attach(GTK_GRID(grid), sliders, 0, 3, 32, 1); +#else + gtk_fixed_put(GTK_FIXED(fixed),sliders,0,y); + y+=SLIDERS_HEIGHT; +#endif + } - toolbar = toolbar_init(display_width,TOOLBAR_HEIGHT,window); - gtk_grid_attach(GTK_GRID(grid), toolbar, 0, 3, 4, 1); + if(display_toolbar) { +fprintf(stderr,"toolbar_height=%d\n",TOOLBAR_HEIGHT); + toolbar = toolbar_init(display_width,TOOLBAR_HEIGHT,window); +#ifdef GRID_LAYOUT + gtk_grid_attach(GTK_GRID(grid), toolbar, 0, 4, 32, 1); +#else + gtk_fixed_put(GTK_FIXED(fixed),toolbar,0,y); + y+=TOOLBAR_HEIGHT; +#endif + } splash_close(); @@ -285,19 +922,8 @@ gint init(void* arg) { gtk_window_fullscreen(GTK_WINDOW(window)); - GdkCursor *cursor=gdk_cursor_new(GDK_ARROW); GdkWindow *gdk_window = gtk_widget_get_window(window); - gdk_window_set_cursor(gdk_window,cursor); - -/* - GdkDisplay *display=gdk_display_get_default(); - if(display==NULL) { - fprintf(stderr,"no default display!\n"); - _exit(0); - } - - GdkCursor *cursor=gdk_cursor_new_for_display(display,GDK_ARROW); -*/ + gdk_window_set_cursor(gdk_window,cursor_arrow); update_timer_id=gdk_threads_add_timeout(1000/updates_per_second, update, NULL); @@ -306,6 +932,10 @@ gint init(void* arg) { vfo_update(NULL); + if(protocol==ORIGINAL_PROTOCOL) { + setFrequency(getFrequency()); + } + return 0; } @@ -316,6 +946,8 @@ main (int argc, { gtk_init (&argc, &argv); + fprintf(stderr,"Build: %s %s\n",build_date,build_time); + uname(&unameData); fprintf(stderr,"sysname: %s\n",unameData.sysname); fprintf(stderr,"nodename: %s\n",unameData.nodename); @@ -330,12 +962,12 @@ main (int argc, } - //display_width=gdk_screen_get_width(screen); - //display_height=gdk_screen_get_height(screen); - display_width=800; - display_height=480; + display_width=gdk_screen_get_width(screen); + display_height=gdk_screen_get_height(screen); + + fprintf(stderr,"display_width=%d display_height=%d\n", display_width, display_height); - splash_show("splash.png", 0, display_width, display_height); + splash_show("hpsdr.png", 0, display_width, display_height); g_idle_add(init,(void *)argv[0]); diff --git a/main.h b/main.h index 1e1d955..dff0089 100644 --- a/main.h +++ b/main.h @@ -1,2 +1,21 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include extern struct utsname unameData; diff --git a/menu.c b/menu.c new file mode 100644 index 0000000..317d867 --- /dev/null +++ b/menu.c @@ -0,0 +1,532 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +#include +#include +#include + +#include "band.h" +#include "discovered.h" +#include "new_protocol.h" +#include "radio.h" +#include "version.h" + +static GtkWidget *parent_window; + +static GtkWidget *box; +static GtkWidget *menu; + +static void cw_keyer_internal_cb(GtkWidget *widget, gpointer data) { + cw_keyer_internal=cw_keyer_internal==1?0:1; + cw_changed(); +} + +static void cw_keyer_speed_value_changed_cb(GtkWidget *widget, gpointer data) { + cw_keyer_speed=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); + cw_changed(); +} + +static void cw_breakin_cb(GtkWidget *widget, gpointer data) { + cw_breakin=cw_breakin==1?0:1; + cw_changed(); +} + +static void cw_keyer_hang_time_value_changed_cb(GtkWidget *widget, gpointer data) { + cw_keyer_hang_time=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); + cw_changed(); +} + +static void cw_keys_reversed_cb(GtkWidget *widget, gpointer data) { + cw_keys_reversed=cw_keys_reversed==1?0:1; + cw_changed(); +} + +static void cw_keyer_mode_cb(GtkWidget *widget, gpointer data) { + cw_keyer_mode=(int)data; + cw_changed(); +} + +static void vfo_divisor_value_changed_cb(GtkWidget *widget, gpointer data) { + vfo_encoder_divisor=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void panadapter_high_value_changed_cb(GtkWidget *widget, gpointer data) { + panadapter_high=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void panadapter_low_value_changed_cb(GtkWidget *widget, gpointer data) { + panadapter_low=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void waterfall_high_value_changed_cb(GtkWidget *widget, gpointer data) { + waterfall_high=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void waterfall_low_value_changed_cb(GtkWidget *widget, gpointer data) { + waterfall_low=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void waterfall_automatic_cb(GtkWidget *widget, gpointer data) { + waterfall_automatic=waterfall_automatic==1?0:1; +} + +static void linein_cb(GtkWidget *widget, gpointer data) { + mic_linein=mic_linein==1?0:1; +} + +static void micboost_cb(GtkWidget *widget, gpointer data) { + mic_boost=mic_boost==1?0:1; +} + +static void ptt_cb(GtkWidget *widget, gpointer data) { + mic_ptt_enabled=mic_ptt_enabled==1?0:1; +} + +static void ptt_ring_cb(GtkWidget *widget, gpointer data) { + mic_ptt_tip_bias_ring=0; +} + +static void ptt_tip_cb(GtkWidget *widget, gpointer data) { + mic_ptt_tip_bias_ring=1; +} + +static void bias_cb(GtkWidget *widget, gpointer data) { + mic_bias_enabled=mic_bias_enabled==1?0:1; +} + +static void apollo_cb(GtkWidget *widget, gpointer data); + +static void alex_cb(GtkWidget *widget, gpointer data) { + if(filter_board==ALEX) { + filter_board=NONE; + } else if(filter_board==NONE) { + filter_board=ALEX; + } else if(filter_board==APOLLO) { + GtkWidget *w=(GtkWidget *)data; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE); + filter_board=ALEX; + } + + if(protocol==NEW_PROTOCOL) { + filter_board_changed(); + } +} + +static void apollo_cb(GtkWidget *widget, gpointer data) { + if(filter_board==APOLLO) { + filter_board=NONE; + } else if(filter_board==NONE) { + filter_board=APOLLO; + } else if(filter_board==ALEX) { + GtkWidget *w=(GtkWidget *)data; + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE); + filter_board=APOLLO; + } + if(protocol==NEW_PROTOCOL) { + filter_board_changed(); + } +} + +static void apollo_tuner_cb(GtkWidget *widget, gpointer data) { + apollo_tuner=apollo_tuner==1?0:1; + if(protocol==NEW_PROTOCOL) { + tuner_changed(); + } +} + +static void pa_cb(GtkWidget *widget, gpointer data) { + pa=pa==1?0:1; + if(protocol==NEW_PROTOCOL) { + pa_changed(); + } +} + + +static void tx_out_of_band_cb(GtkWidget *widget, gpointer data) { + tx_out_of_band=tx_out_of_band==1?0:1; +} + +static void pa_value_changed_cb(GtkWidget *widget, gpointer data) { + BAND *band=(BAND *)data; + band->pa_calibration=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static gboolean exit_pressed_event_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + gpio_close(); + if(protocol==ORIGINAL_PROTOCOL) { + old_protocol_stop(); + } else { + new_protocol_stop(); + } + _exit(0); +} + +static gboolean menu_pressed_event_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + int i; + int id; + + GtkWidget *dialog=gtk_dialog_new_with_buttons("Menu",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + + GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + + + + GtkWidget *notebook=gtk_notebook_new(); + + + GtkWidget *display_label=gtk_label_new("Display"); + GtkWidget *display_grid=gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(display_grid),TRUE); + + GtkWidget *panadapter_high_label=gtk_label_new("Panadapter High: "); + //gtk_widget_override_font(panadapter_high_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(panadapter_high_label); + gtk_grid_attach(GTK_GRID(display_grid),panadapter_high_label,0,1,1,1); + + GtkWidget *panadapter_high_r=gtk_spin_button_new_with_range(-220.0,100.0,1.0); + //gtk_widget_override_font(panadapter_high_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(panadapter_high_r),(double)panadapter_high); + gtk_widget_show(panadapter_high_r); + gtk_grid_attach(GTK_GRID(display_grid),panadapter_high_r,1,1,1,1); + g_signal_connect(panadapter_high_r,"value_changed",G_CALLBACK(panadapter_high_value_changed_cb),NULL); + + GtkWidget *panadapter_low_label=gtk_label_new("Panadapter Low: "); + //gtk_widget_override_font(panadapter_low_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(panadapter_low_label); + gtk_grid_attach(GTK_GRID(display_grid),panadapter_low_label,0,2,1,1); + + GtkWidget *panadapter_low_r=gtk_spin_button_new_with_range(-220.0,100.0,1.0); + //gtk_widget_override_font(panadapter_low_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(panadapter_low_r),(double)panadapter_low); + gtk_widget_show(panadapter_low_r); + gtk_grid_attach(GTK_GRID(display_grid),panadapter_low_r,1,2,1,1); + g_signal_connect(panadapter_low_r,"value_changed",G_CALLBACK(panadapter_low_value_changed_cb),NULL); + + GtkWidget *waterfall_automatic_label=gtk_label_new("Waterfall Automatic: "); + //gtk_widget_override_font(waterfall_automatic_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(waterfall_automatic_label); + gtk_grid_attach(GTK_GRID(display_grid),waterfall_automatic_label,0,3,1,1); + + GtkWidget *waterfall_automatic_b=gtk_check_button_new(); + //gtk_widget_override_font(waterfall_automatic_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (waterfall_automatic_b), waterfall_automatic); + gtk_widget_show(waterfall_automatic_b); + gtk_grid_attach(GTK_GRID(display_grid),waterfall_automatic_b,1,3,1,1); + g_signal_connect(waterfall_automatic_b,"toggled",G_CALLBACK(waterfall_automatic_cb),NULL); + + GtkWidget *waterfall_high_label=gtk_label_new("Waterfall High: "); + //gtk_widget_override_font(waterfall_high_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(waterfall_high_label); + gtk_grid_attach(GTK_GRID(display_grid),waterfall_high_label,0,4,1,1); + + GtkWidget *waterfall_high_r=gtk_spin_button_new_with_range(-220.0,100.0,1.0); + //gtk_widget_override_font(waterfall_high_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(waterfall_high_r),(double)waterfall_high); + gtk_widget_show(waterfall_high_r); + gtk_grid_attach(GTK_GRID(display_grid),waterfall_high_r,1,4,1,1); + g_signal_connect(waterfall_high_r,"value_changed",G_CALLBACK(waterfall_high_value_changed_cb),NULL); + + GtkWidget *waterfall_low_label=gtk_label_new("Waterfall Low: "); + //gtk_widget_override_font(waterfall_low_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(waterfall_low_label); + gtk_grid_attach(GTK_GRID(display_grid),waterfall_low_label,0,5,1,1); + + GtkWidget *waterfall_low_r=gtk_spin_button_new_with_range(-220.0,100.0,1.0); + //gtk_widget_override_font(waterfall_low_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(waterfall_low_r),(double)waterfall_low); + gtk_widget_show(waterfall_low_r); + gtk_grid_attach(GTK_GRID(display_grid),waterfall_low_r,1,5,1,1); + g_signal_connect(waterfall_low_r,"value_changed",G_CALLBACK(waterfall_low_value_changed_cb),NULL); + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),display_grid,display_label); + + + + GtkWidget *tx_label=gtk_label_new("TX"); + GtkWidget *tx_grid=gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(tx_grid),TRUE); + gtk_grid_set_column_spacing (GTK_GRID(tx_grid),10); + + for(i=0;ititle); + //gtk_widget_override_font(band_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(band_label); + gtk_grid_attach(GTK_GRID(tx_grid),band_label,(i/6)*2,i%6,1,1); + + GtkWidget *pa_r=gtk_spin_button_new_with_range(0.0,100.0,1.0); + //gtk_widget_override_font(pa_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(pa_r),(double)band->pa_calibration); + gtk_widget_show(pa_r); + gtk_grid_attach(GTK_GRID(tx_grid),pa_r,((i/6)*2)+1,i%6,1,1); + g_signal_connect(pa_r,"value_changed",G_CALLBACK(pa_value_changed_cb),band); + } + + GtkWidget *tx_out_of_band_b=gtk_check_button_new_with_label("Transmit out of band"); + //gtk_widget_override_font(tx_out_of_band_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tx_out_of_band_b), tx_out_of_band); + gtk_widget_show(tx_out_of_band_b); + gtk_grid_attach(GTK_GRID(tx_grid),tx_out_of_band_b,0,7,4,1); + g_signal_connect(tx_out_of_band_b,"toggled",G_CALLBACK(tx_out_of_band_cb),NULL); + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),tx_grid,tx_label); + + GtkWidget *cw_label=gtk_label_new("CW"); + GtkWidget *cw_grid=gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(cw_grid),TRUE); + + GtkWidget *cw_keyer_internal_b=gtk_check_button_new_with_label("CW Internal - Speed (WPM)"); + gtk_widget_override_font(cw_keyer_internal_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_internal_b), cw_keyer_internal); + gtk_widget_show(cw_keyer_internal_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_internal_b,0,0,1,1); + g_signal_connect(cw_keyer_internal_b,"toggled",G_CALLBACK(cw_keyer_internal_cb),NULL); + + GtkWidget *cw_keyer_speed_b=gtk_spin_button_new_with_range(1.0,60.0,1.0); + gtk_widget_override_font(cw_keyer_speed_b, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_speed_b),(double)cw_keyer_speed); + gtk_widget_show(cw_keyer_speed_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_speed_b,1,0,1,1); + g_signal_connect(cw_keyer_speed_b,"value_changed",G_CALLBACK(cw_keyer_speed_value_changed_cb),NULL); + + GtkWidget *cw_breakin_b=gtk_check_button_new_with_label("CW Break In - Delay (ms)"); + gtk_widget_override_font(cw_breakin_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_breakin_b), cw_breakin); + gtk_widget_show(cw_breakin_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_breakin_b,0,1,1,1); + g_signal_connect(cw_breakin_b,"toggled",G_CALLBACK(cw_breakin_cb),NULL); + + GtkWidget *cw_keyer_hang_time_b=gtk_spin_button_new_with_range(0.0,1000.0,1.0); + gtk_widget_override_font(cw_keyer_hang_time_b, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_hang_time_b),(double)cw_keyer_hang_time); + gtk_widget_show(cw_keyer_hang_time_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_hang_time_b,1,1,1,1); + g_signal_connect(cw_keyer_hang_time_b,"value_changed",G_CALLBACK(cw_keyer_hang_time_value_changed_cb),NULL); + + GtkWidget *cw_keyer_straight=gtk_radio_button_new_with_label(NULL,"CW KEYER STRAIGHT"); + gtk_widget_override_font(cw_keyer_straight, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_straight), cw_keyer_mode==KEYER_STRAIGHT); + gtk_widget_show(cw_keyer_straight); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_straight,0,2,1,1); + g_signal_connect(cw_keyer_straight,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_STRAIGHT); + + GtkWidget *cw_keyer_mode_a=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cw_keyer_straight),"CW KEYER MODE A"); + gtk_widget_override_font(cw_keyer_mode_a, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_mode_a), cw_keyer_mode==KEYER_MODE_A); + gtk_widget_show(cw_keyer_mode_a); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_mode_a,0,3,1,1); + g_signal_connect(cw_keyer_mode_a,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_MODE_A); + + GtkWidget *cw_keyer_mode_b=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cw_keyer_mode_a),"CW KEYER MODE B"); + gtk_widget_override_font(cw_keyer_mode_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_mode_b), cw_keyer_mode==KEYER_MODE_B); + gtk_widget_show(cw_keyer_mode_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_mode_b,0,4,1,1); + g_signal_connect(cw_keyer_mode_b,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_MODE_B); + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),cw_grid,cw_label); + + + GtkWidget *encoder_label=gtk_label_new("VFO Encoder"); + GtkWidget *encoder_grid=gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(encoder_grid),TRUE); + + GtkWidget *vfo_divisor_label=gtk_label_new("Divisor: "); + gtk_widget_override_font(vfo_divisor_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(vfo_divisor_label); + gtk_grid_attach(GTK_GRID(encoder_grid),vfo_divisor_label,0,0,1,1); + + GtkWidget *vfo_divisor=gtk_spin_button_new_with_range(1.0,60.0,1.0); + gtk_widget_override_font(vfo_divisor, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(vfo_divisor),(double)vfo_encoder_divisor); + gtk_widget_show(vfo_divisor); + gtk_grid_attach(GTK_GRID(encoder_grid),vfo_divisor,1,0,1,1); + g_signal_connect(vfo_divisor,"value_changed",G_CALLBACK(vfo_divisor_value_changed_cb),NULL); + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),encoder_grid,encoder_label); + + GtkWidget *microphone_label=gtk_label_new("Microphone"); + GtkWidget *microphone_grid=gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(microphone_grid),TRUE); + + GtkWidget *linein_b=gtk_check_button_new_with_label("Line In"); + //gtk_widget_override_font(linein_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (linein_b), mic_linein); + gtk_widget_show(linein_b); + gtk_grid_attach(GTK_GRID(microphone_grid),linein_b,1,1,1,1); + g_signal_connect(linein_b,"toggled",G_CALLBACK(linein_cb),NULL); + + GtkWidget *micboost_b=gtk_check_button_new_with_label("Boost"); + //gtk_widget_override_font(micboost_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (micboost_b), mic_boost); + gtk_widget_show(micboost_b); + gtk_grid_attach(GTK_GRID(microphone_grid),micboost_b,1,2,1,1); + g_signal_connect(micboost_b,"toggled",G_CALLBACK(micboost_cb),NULL); + + + if((protocol==NEW_PROTOCOL && device==NEW_DEVICE_ORION) || (protocol==ORIGINAL_PROTOCOL && device==DEVICE_ORION)) { + + GtkWidget *ptt_ring_b=gtk_radio_button_new_with_label(NULL,"PTT On Ring, Mic and Bias on Tip"); + //gtk_widget_override_font(ptt_ring_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_ring_b), mic_ptt_tip_bias_ring==0); + gtk_widget_show(ptt_ring_b); + gtk_grid_attach(GTK_GRID(microphone_grid),ptt_ring_b,1,3,1,1); + g_signal_connect(ptt_ring_b,"pressed",G_CALLBACK(ptt_ring_cb),NULL); + + GtkWidget *ptt_tip_b=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ptt_ring_b),"PTT On Tip, Mic and Bias on Ring"); + //gtk_widget_override_font(ptt_tip_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_tip_b), mic_ptt_tip_bias_ring==1); + gtk_widget_show(ptt_tip_b); + gtk_grid_attach(GTK_GRID(microphone_grid),ptt_tip_b,1,4,1,1); + g_signal_connect(ptt_tip_b,"pressed",G_CALLBACK(ptt_tip_cb),NULL); + + GtkWidget *ptt_b=gtk_check_button_new_with_label("PTT Enabled"); + //gtk_widget_override_font(ptt_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_b), mic_ptt_enabled); + gtk_widget_show(ptt_b); + gtk_grid_attach(GTK_GRID(microphone_grid),ptt_b,1,5,1,1); + g_signal_connect(ptt_b,"toggled",G_CALLBACK(ptt_cb),NULL); + + GtkWidget *bias_b=gtk_check_button_new_with_label("BIAS Enabled"); + //gtk_widget_override_font(bias_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bias_b), mic_bias_enabled); + gtk_widget_show(bias_b); + gtk_grid_attach(GTK_GRID(microphone_grid),bias_b,1,6,1,1); + g_signal_connect(bias_b,"toggled",G_CALLBACK(bias_cb),NULL); + } + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),microphone_grid,microphone_label); + + GtkWidget *filters_label=gtk_label_new("Filters/PA"); + GtkWidget *filters_grid=gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(filters_grid),TRUE); + + GtkWidget *alex_b=gtk_check_button_new_with_label("ALEX"); + //gtk_widget_override_font(alex_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (alex_b), filter_board==ALEX); + gtk_widget_show(alex_b); + gtk_grid_attach(GTK_GRID(filters_grid),alex_b,2,1,1,1); + + GtkWidget *apollo_b=gtk_check_button_new_with_label("APOLLO"); + //gtk_widget_override_font(apollo_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apollo_b), filter_board==APOLLO); + gtk_widget_show(apollo_b); + gtk_grid_attach(GTK_GRID(filters_grid),apollo_b,2,2,1,1); + + g_signal_connect(alex_b,"toggled",G_CALLBACK(alex_cb),apollo_b); + g_signal_connect(apollo_b,"toggled",G_CALLBACK(apollo_cb),alex_b); + + GtkWidget *apollo_tuner_b=gtk_check_button_new_with_label("Auto Tuner"); + //gtk_widget_override_font(apollo_tuner_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apollo_tuner_b), apollo_tuner); + gtk_widget_show(apollo_tuner_b); + gtk_grid_attach(GTK_GRID(filters_grid),apollo_tuner_b,2,3,1,1); + g_signal_connect(apollo_tuner_b,"toggled",G_CALLBACK(apollo_tuner_cb),NULL); + + GtkWidget *pa_b=gtk_check_button_new_with_label("PA"); + //gtk_widget_override_font(pa_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pa_b), pa); + gtk_widget_show(pa_b); + gtk_grid_attach(GTK_GRID(filters_grid),pa_b,2,4,1,1); + g_signal_connect(pa_b,"toggled",G_CALLBACK(pa_cb),NULL); + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),filters_grid,filters_label); + + + GtkWidget *exit_label=gtk_label_new("Exit"); + GtkWidget *exit_grid=gtk_grid_new(); + //gtk_grid_set_row_homogeneous(GTK_GRID(exit_grid),TRUE); + + GtkWidget *exit_button=gtk_button_new_with_label("Exit PiHPSDR"); + g_signal_connect (exit_button, "pressed", G_CALLBACK(exit_pressed_event_cb), NULL); + gtk_grid_attach(GTK_GRID(exit_grid),exit_button,0,0,1,1); + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),exit_grid,exit_label); + + + +/* + GtkWidget *about_label=gtk_label_new("About"); + GtkWidget *about_grid=gtk_grid_new(); + gtk_grid_set_column_homogeneous(GTK_GRID(about_grid),TRUE); + + char build[64]; + + sprintf(build,"build: %s %s",build_date, build_time); + GtkWidget *pi_label=gtk_label_new("pihpsdr by John Melton g0orx/n6lyt"); + gtk_widget_show(pi_label); + gtk_grid_attach(GTK_GRID(about_grid),pi_label,0,0,1,1); + GtkWidget *filler_label=gtk_label_new(""); + gtk_widget_show(filler_label); + gtk_grid_attach(GTK_GRID(about_grid),filler_label,0,1,1,1); + GtkWidget *build_date_label=gtk_label_new(build); + gtk_widget_show(build_date_label); + gtk_grid_attach(GTK_GRID(about_grid),build_date_label,0,2,1,1); + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),about_grid,about_label); +*/ + gtk_container_add(GTK_CONTAINER(content),notebook); + + GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close Dialog",GTK_RESPONSE_OK); + // gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18")); + + gtk_widget_show_all(dialog); + + int result=gtk_dialog_run(GTK_DIALOG(dialog)); + + radioSaveState(); + + gtk_widget_destroy (dialog); + + return TRUE; + +} + +GtkWidget* menu_init(int width,int height,GtkWidget *parent) { + + GdkRGBA black; + black.red=0.0; + black.green=0.0; + black.blue=0.0; + black.alpha=0.0; + + fprintf(stderr,"menu_init: width=%d height=%d\n",width,height); + + parent_window=parent; + + box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL,0); + gtk_widget_set_size_request (box, width, height); + gtk_widget_override_background_color(box, GTK_STATE_NORMAL, &black); + + menu=gtk_button_new_with_label("Menu"); + // gtk_widget_set_size_request (menu, width, height); + g_signal_connect (menu, "pressed", G_CALLBACK(menu_pressed_event_cb), NULL); + gtk_box_pack_start (GTK_BOX(box),menu,TRUE,TRUE,0); + + gtk_widget_show_all(box); + + return box; +} diff --git a/menu.h b/menu.h new file mode 100644 index 0000000..90f6b2d --- /dev/null +++ b/menu.h @@ -0,0 +1,21 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + + +GtkWidget* menu_init(int width,int height,GtkWidget *parent); diff --git a/meter.c b/meter.c index 2d77107..5c774bd 100644 --- a/meter.c +++ b/meter.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #include #include @@ -11,6 +30,9 @@ static cairo_surface_t *meter_surface = NULL; static int meter_width; static int meter_height; +static int last_meter_type=SMETER; +static int max_level=-200; +static int max_count=0; static void meter_clear_surface (void) @@ -28,6 +50,9 @@ meter_configure_event_cb (GtkWidget *widget, GdkEventConfigure *event, gpointer data) { +fprintf(stderr,"meter_configure_event_cb: width=%d height=%d\n", + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); if (meter_surface) cairo_surface_destroy (meter_surface); @@ -56,6 +81,7 @@ meter_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer data) { GtkWidget* meter_init(int width,int height) { +fprintf(stderr,"meter_init: width=%d height=%d\n",width,height); meter_width=width; meter_height=height; @@ -73,10 +99,11 @@ GtkWidget* meter_init(int width,int height) { } -void meter_update(int meter_type,double value,double reverse) { +void meter_update(int meter_type,double value,double reverse,double exciter) { char sf[32]; int text_location; + double offset; cairo_t *cr; cr = cairo_create (meter_surface); @@ -84,62 +111,90 @@ void meter_update(int meter_type,double value,double reverse) { cairo_set_source_rgb (cr, 0, 0, 0); cairo_paint (cr); + if(last_meter_type!=meter_type) { + last_meter_type=meter_type; + max_count=0; + if(meter_type==SMETER) { + max_level=-200; + } else { + max_level=0; + } + } + cairo_set_source_rgb(cr, 1, 1, 1); switch(meter_type) { case SMETER: // value is dBm text_location=10; + offset=5.0; cairo_select_font_face(cr, "Arial", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); double level=value+(double)get_attenuation(); if(meter_width>=114) { int db=meter_width/114; // S9+60 (9*6)+60 + if(db>2) db=2; int i; cairo_set_line_width(cr, 1.0); cairo_set_source_rgb(cr, 1, 1, 1); for(i=0;i<54;i++) { - cairo_move_to(cr,(double)(i*db),(double)meter_height-20); + cairo_move_to(cr,offset+(double)(i*db),(double)meter_height-20); if(i%18==0) { - cairo_line_to(cr,(double)(i*db),(double)(meter_height-30)); + cairo_line_to(cr,offset+(double)(i*db),(double)(meter_height-30)); } else if(i%6==0) { - cairo_line_to(cr,(double)(i*db),(double)(meter_height-25)); + cairo_line_to(cr,offset+(double)(i*db),(double)(meter_height-25)); } } cairo_stroke(cr); cairo_set_font_size(cr, 12); - cairo_move_to(cr, (double)(18*db)-3.0, (double)meter_height); + cairo_move_to(cr, offset+(double)(18*db)-3.0, (double)meter_height); cairo_show_text(cr, "3"); - cairo_move_to(cr, (double)(36*db)-3.0, (double)meter_height); + cairo_move_to(cr, offset+(double)(36*db)-3.0, (double)meter_height); cairo_show_text(cr, "6"); cairo_set_source_rgb(cr, 1, 0, 0); - cairo_move_to(cr,(double)(54*db),(double)meter_height-20); - cairo_line_to(cr,(double)(54*db),(double)(meter_height-30)); - cairo_move_to(cr,(double)(74*db),(double)meter_height-20); - cairo_line_to(cr,(double)(74*db),(double)(meter_height-30)); - cairo_move_to(cr,(double)(94*db),(double)meter_height-20); - cairo_line_to(cr,(double)(94*db),(double)(meter_height-30)); - cairo_move_to(cr,(double)(114*db),(double)meter_height-20); - cairo_line_to(cr,(double)(114*db),(double)(meter_height-30)); + cairo_move_to(cr,offset+(double)(54*db),(double)meter_height-20); + cairo_line_to(cr,offset+(double)(54*db),(double)(meter_height-30)); + cairo_move_to(cr,offset+(double)(74*db),(double)meter_height-20); + cairo_line_to(cr,offset+(double)(74*db),(double)(meter_height-30)); + cairo_move_to(cr,offset+(double)(94*db),(double)meter_height-20); + cairo_line_to(cr,offset+(double)(94*db),(double)(meter_height-30)); + cairo_move_to(cr,offset+(double)(114*db),(double)meter_height-20); + cairo_line_to(cr,offset+(double)(114*db),(double)(meter_height-30)); cairo_stroke(cr); - cairo_move_to(cr, (double)(54*db)-3.0, (double)meter_height); + cairo_move_to(cr, offset+(double)(54*db)-3.0, (double)meter_height); cairo_show_text(cr, "9"); - cairo_move_to(cr, (double)(74*db)-12.0, (double)meter_height); + cairo_move_to(cr, offset+(double)(74*db)-12.0, (double)meter_height); cairo_show_text(cr, "+20"); - cairo_move_to(cr, (double)(94*db)-9.0, (double)meter_height); + cairo_move_to(cr, offset+(double)(94*db)-9.0, (double)meter_height); cairo_show_text(cr, "+40"); - cairo_move_to(cr, (double)(114*db)-6.0, (double)meter_height); + cairo_move_to(cr, offset+(double)(114*db)-6.0, (double)meter_height); cairo_show_text(cr, "+60"); cairo_set_source_rgb(cr, 0, 1, 0); - cairo_move_to(cr,(double)((level+127.0)*db),(double)meter_height-30); - cairo_line_to(cr,(double)((level+127.0)*db),(double)(meter_height-50)); + cairo_rectangle(cr, offset+0.0, (double)(meter_height-50), (double)((level+127.0)*db), 20.0); + cairo_fill(cr); + + if(level>max_level || max_count==10) { + max_level=level; + max_count=0; + } + + if(max_level!=0) { + cairo_set_source_rgb(cr, 1, 1, 0); + cairo_move_to(cr,offset+(double)((max_level+127.0)*db),(double)meter_height-30); + cairo_line_to(cr,offset+(double)((max_level+127.0)*db),(double)(meter_height-50)); + } + + cairo_stroke(cr); - text_location=(db*114)+5; + max_count++; + + + text_location=offset+(db*114)+5; } cairo_set_font_size(cr, 16); @@ -154,18 +209,23 @@ void meter_update(int meter_type,double value,double reverse) { CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 18); - sprintf(sf,"FWD: %3.2f W",value); + if((int)value>max_level || max_count==10) { + max_level=(int)value; + max_count=0; + } + max_count++; + + sprintf(sf,"FWD: %d W",(int)max_level); cairo_move_to(cr, 10, 25); cairo_show_text(cr, sf); - // value is Watts double swr=(value+reverse)/(value-reverse); cairo_select_font_face(cr, "Arial", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size(cr, 18); - sprintf(sf,"SWR: %1.2f:1",swr); + sprintf(sf,"SWR: %1.1f:1",swr); cairo_move_to(cr, 10, 45); cairo_show_text(cr, sf); diff --git a/meter.h b/meter.h index ad197d3..eb6c57b 100644 --- a/meter.h +++ b/meter.h @@ -1,6 +1,25 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #define SMETER 0 #define POWER 1 GtkWidget* meter_init(int width,int height); -void meter_update(int meter_type,double value,double reverse); +void meter_update(int meter_type,double value,double reverse,double exciter); diff --git a/mode.c b/mode.c index 98fdfa5..9b6048f 100644 --- a/mode.c +++ b/mode.c @@ -1,2 +1,21 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + char *mode_string[]={"LSB","USB","DSB","CWL","CWU","FMN","AM","DIGU","SPEC","DIGL","SAM","DRM"}; diff --git a/mode.h b/mode.h index a4766a0..154bab6 100644 --- a/mode.h +++ b/mode.h @@ -1,14 +1,6 @@ -/** -* @file mode.h -* @brief Header files for the mode functions -* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S -* @version 0.1 -* @date 2009-04-12 -*/ -// mode.h - /* Copyright (C) -* 2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S +* 2015 - John Melton, G0ORX/N6LYT +* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 diff --git a/new_discovery.c b/new_discovery.c index fe4a05a..c69a10a 100644 --- a/new_discovery.c +++ b/new_discovery.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #include #include @@ -17,9 +36,6 @@ #include "discovered.h" //#include "discovery.h" -int selected_device=0; -int devices=0; -DISCOVERED discovered[MAX_DEVICES]; static char interface_name[64]; static struct sockaddr_in interface_addr={0}; @@ -51,12 +67,11 @@ void print_device(int i) { } void new_discovery() { - devices=0; - selected_device=0; struct ifaddrs *addrs,*ifa; getifaddrs(&addrs); ifa = addrs; while (ifa) { + g_main_context_iteration(NULL, 0); if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) { if((ifa->ifa_flags&IFF_UP)==IFF_UP && (ifa->ifa_flags&IFF_RUNNING)==IFF_RUNNING @@ -165,7 +180,7 @@ void* new_discover_receive_thread(void* arg) { struct timeval tv; int i; - tv.tv_sec = 1; + tv.tv_sec = 2; tv.tv_usec = 0; setsockopt(discovery_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)); diff --git a/new_discovery.h b/new_discovery.h index fefa81a..84b98dc 100644 --- a/new_discovery.h +++ b/new_discovery.h @@ -1,2 +1,21 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + void new_discovery(void); diff --git a/new_protocol.c b/new_protocol.c index a3b9687..dab0506 100644 --- a/new_protocol.c +++ b/new_protocol.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + //#define ECHO_MIC @@ -25,17 +44,20 @@ #include "channel.h" #include "discovered.h" #include "wdsp.h" +#include "mode.h" +#include "filter.h" #include "radio.h" #include "vfo.h" #include "toolbar.h" +#include "wdsp_init.h" #define PI 3.1415926535897932F -static int receiver; -static int running=0; - int data_socket; +static int receiver; +static int running; + static struct sockaddr_in base_addr; static int base_addr_length; @@ -69,21 +91,8 @@ static long general_sequence = 0; static long rx_specific_sequence = 0; static long tx_specific_sequence = 0; -static int lt2208Dither = 0; -static int lt2208Random = 0; -static int attenuation = 20; // 20dB - -static long long ddsAFrequency = 14250000; -static int filterLow=150; -static int filterHigh=2550; -static int mode=modeUSB; -static unsigned long alex_rx_antenna=0; -static unsigned long alex_tx_antenna=0; -static unsigned long alex_attenuation=0; - static int buffer_size=BUFFER_SIZE; static int fft_size=4096; -static int sampleRate=48000; static int dspRate=48000; static int outputRate=48000; @@ -94,165 +103,46 @@ static int micOutputRate=192000; static int spectrumWIDTH=800; static int SPECTRUM_UPDATES_PER_SECOND=10; -static int mox=0; -static int tune=0; static float phase = 0.0F; -static int ptt; -static int dot; -static int dash; -static int pll_locked; -static int adc_overload; -unsigned int exciter_power; -unsigned int alex_forward_power; -unsigned int alex_reverse_power; -static int supply_volts; - long response_sequence; int response; +sem_t send_high_priority_sem; int send_high_priority=0; +sem_t send_general_sem; int send_general=0; -static void initAnalyzer(int channel,int buffer_size); static void* new_protocol_thread(void* arg); static void* new_protocol_timer_thread(void* arg); -void filter_board_changed() { - send_general=1; -} - -void pa_changed() { - send_general=1; -} - -void tuner_changed() { - send_general=1; -} - -void cw_changed() { -} - -void set_attenuation(int value) { - attenuation=value; -} - -int get_attenuation() { - return attenuation; -} - -void set_alex_rx_antenna(unsigned long v) { - alex_rx_antenna=v; - send_high_priority=1; -} - -void set_alex_tx_antenna(unsigned long v) { - alex_tx_antenna=v; - send_high_priority=1; -} - -void set_alex_attenuation(unsigned long v) { - alex_attenuation=v; +void schedule_high_priority(int source) { +fprintf(stderr,"new_protocol: schedule_high_priority: source=%d\n",source); + sem_wait(&send_high_priority_sem); send_high_priority=1; + sem_post(&send_high_priority_sem); } -void setSampleRate(int rate) { - sampleRate=rate; -} - -int getSampleRate() { - return sampleRate; -} - -void setFrequency(long long f) { - ddsAFrequency=f; - send_high_priority=1; -} - -long long getFrequency() { - return ddsAFrequency; -} - -void setMode(int m) { - mode=m; - SetRXAMode(receiver, mode); - SetTXAMode(CHANNEL_TX, mode); -} - -int getMode() { - return mode; -} - -void setFilter(int low,int high) { - if(mode==modeCWL) { - filterLow=-cwPitch-low; - filterHigh=-cwPitch+high; - } else if(mode==modeCWU) { - filterLow=cwPitch-low; - filterHigh=cwPitch+high; - } else { - filterLow=low; - filterHigh=high; - } - - RXANBPSetFreqs(receiver,(double)filterLow,(double)filterHigh); - SetRXABandpassFreqs(receiver, (double)filterLow, (double)filterHigh); - SetRXASNBAOutputBandwidth(receiver, (double)filterLow, (double)filterHigh); - - SetTXABandpassFreqs(CHANNEL_TX, (double)filterLow, (double)filterHigh); -} - -int getFilterLow() { - return filterLow; -} - -int getFilterHigh() { - return filterHigh; -} - -void setMox(int state) { - if(mox!=state) { - mox=state; - send_high_priority=1; - } -} - -int getMox() { - return mox; -} - -void setTune(int state) { - if(tune!=state) { - tune=state; - send_high_priority=1; - send_general=1; - } -} - -int getTune() { - return tune; -} - -int isTransmitting() { - return ptt!=0 || mox!=0 || tune!=0; +void schedule_general() { +fprintf(stderr,"new_protocol: schedule_general\n"); + sem_wait(&send_general_sem); + send_general=1; + sem_post(&send_general_sem); } -double getDrive() { - return (double)drive/255.0; +void filter_board_changed() { + schedule_general(); } -void setDrive(double value) { - drive=(int)(value*255.0); - send_high_priority=1; +void pa_changed() { + schedule_general(); } -double getTuneDrive() { - return (double)tune_drive/255.0; +void tuner_changed() { + schedule_general(); } -void setTuneDrive(double value) { - tune_drive=(int)(value*255.0); - send_high_priority=1; +void cw_changed() { } void new_protocol_init(int rx,int pixels) { @@ -263,56 +153,8 @@ void new_protocol_init(int rx,int pixels) { fprintf(stderr,"new_protocol_init: %d\n",rx); rc=sem_init(&response_sem, 0, 0); - - fprintf(stderr,"OpenChannel %d buffer_size=%d fft_size=%d sampleRate=%d dspRate=%d outputRate=%d\n", - rx, - buffer_size, - fft_size, - sampleRate, - dspRate, - outputRate); - - OpenChannel(rx, - buffer_size, - fft_size, - sampleRate, - dspRate, - outputRate, - 0, // receive - 1, // run - 0.010, 0.025, 0.0, 0.010, 0); - - fprintf(stderr,"OpenChannel %d buffer_size=%d fft_size=%d sampleRate=%d dspRate=%d outputRate=%d\n", - CHANNEL_TX, - buffer_size, - fft_size, - micSampleRate, - micDspRate, - micOutputRate); - - OpenChannel(CHANNEL_TX, - buffer_size, - fft_size, - micSampleRate, - micDspRate, - micOutputRate, - 1, // transmit - 1, // run - 0.010, 0.025, 0.0, 0.010, 0); - - fprintf(stderr,"XCreateAnalyzer %d\n",rx); - int success; - XCreateAnalyzer(rx, &success, 262144, 1, 1, ""); - if (success != 0) { - fprintf(stderr, "XCreateAnalyzer %d failed: %d\n" ,rx,success); - } - initAnalyzer(rx,buffer_size); - - XCreateAnalyzer(CHANNEL_TX, &success, 262144, 1, 1, ""); - if (success != 0) { - fprintf(stderr, "XCreateAnalyzer CHANNEL_TX failed: %d\n" ,success); - } - initAnalyzer(CHANNEL_TX,BUFFER_SIZE); + rc=sem_init(&send_high_priority_sem, 0, 1); + rc=sem_init(&send_general_sem, 0, 1); rc=pthread_create(&new_protocol_thread_id,NULL,new_protocol_thread,NULL); if(rc != 0) { @@ -320,88 +162,8 @@ void new_protocol_init(int rx,int pixels) { exit(-1); } - SetRXAMode(rx, mode); - SetRXABandpassFreqs(rx, (double)filterLow, (double)filterHigh); - SetRXAAGCMode(rx, agc); - SetRXAAGCTop(rx,agc_gain); - - SetRXAAMDSBMode(CHANNEL_RX0, 0); - SetRXAShiftRun(CHANNEL_RX0, 0); - SetRXAEMNRgainMethod(CHANNEL_RX0, 1); - SetRXAEMNRnpeMethod(CHANNEL_RX0, 0); - SetRXAEMNRaeRun(CHANNEL_RX0, 1); - SetRXAEMNRPosition(CHANNEL_RX0, 0); - SetRXAEMNRRun(CHANNEL_RX0, 0); - SetRXAANRRun(CHANNEL_RX0, 0); - SetRXAANFRun(CHANNEL_RX0, 0); - - SetTXAMode(CHANNEL_TX, mode); - SetTXABandpassFreqs(CHANNEL_TX, (double)filterLow, (double)filterHigh); - SetTXABandpassWindow(CHANNEL_TX, 1); - SetTXABandpassRun(CHANNEL_TX, 1); - - SetTXACFIRRun(CHANNEL_TX, 1); - SetTXAEQRun(CHANNEL_TX, 0); - SetTXACTCSSRun(CHANNEL_TX, 0); - SetTXAAMSQRun(CHANNEL_TX, 0); - SetTXACompressorRun(CHANNEL_TX, 0); - SetTXAosctrlRun(CHANNEL_TX, 0); - SetTXAPreGenRun(CHANNEL_TX, 0); - SetTXAPostGenRun(CHANNEL_TX, 0); - } -static void initAnalyzer(int channel,int buffer_size) { - int flp[] = {0}; - double KEEP_TIME = 0.1; - int spur_elimination_ffts = 1; - int data_type = 1; - int fft_size = 8192; - int window_type = 4; - double kaiser_pi = 14.0; - int overlap = 2048; - int clip = 0; - int span_clip_l = 0; - int span_clip_h = 0; - int pixels=spectrumWIDTH; - int stitches = 1; - int avm = 0; - double tau = 0.001 * 120.0; - int MAX_AV_FRAMES = 60; - int display_average = MAX(2, (int) MIN((double) MAX_AV_FRAMES, (double) SPECTRUM_UPDATES_PER_SECOND * tau)); - double avb = exp(-1.0 / (SPECTRUM_UPDATES_PER_SECOND * tau)); - int calibration_data_set = 0; - double span_min_freq = 0.0; - double span_max_freq = 0.0; - - int max_w = fft_size + (int) MIN(KEEP_TIME * (double) SPECTRUM_UPDATES_PER_SECOND, KEEP_TIME * (double) fft_size * (double) SPECTRUM_UPDATES_PER_SECOND); - - fprintf(stderr,"SetAnalyzer channel=%d\n",channel); - SetAnalyzer(channel, - spur_elimination_ffts, //number of LO frequencies = number of ffts used in elimination - data_type, //0 for real input data (I only); 1 for complex input data (I & Q) - flp, //vector with one elt for each LO frequency, 1 if high-side LO, 0 otherwise - fft_size, //size of the fft, i.e., number of input samples - buffer_size, //number of samples transferred for each OpenBuffer()/CloseBuffer() - window_type, //integer specifying which window function to use - kaiser_pi, //PiAlpha parameter for Kaiser window - overlap, //number of samples each fft (other than the first) is to re-use from the previous - clip, //number of fft output bins to be clipped from EACH side of each sub-span - span_clip_l, //number of bins to clip from low end of entire span - span_clip_h, //number of bins to clip from high end of entire span - pixels, //number of pixel values to return. may be either <= or > number of bins - stitches, //number of sub-spans to concatenate to form a complete span - avm, //averaging mode - display_average, //number of spans to (moving) average for pixel result - avb, //back multiplier for weighted averaging - calibration_data_set, //identifier of which set of calibration data to use - span_min_freq, //frequency at first pixel value8192 - span_max_freq, //frequency at last pixel value - max_w //max samples to hold in input ring buffers - ); -} - - static void new_protocol_general() { unsigned char buffer[60]; @@ -446,9 +208,7 @@ static void new_protocol_high_priority(int run,int tx,int drive) { buffer[4]=run|(tx<<1); - extern long long ddsAFrequency; - - long phase=(long)((4294967296.0*(double)ddsAFrequency)/122880000.0); + long phase=(long)((4294967296.0*(double)ddsFrequency)/122880000.0); // rx buffer[9]=phase>>24; @@ -456,7 +216,7 @@ static void new_protocol_high_priority(int run,int tx,int drive) { buffer[11]=phase>>8; buffer[12]=phase; -// tx +// tx (no split yet) buffer[329]=phase>>24; buffer[330]=phase>>16; buffer[331]=phase>>8; @@ -477,15 +237,15 @@ else HPF <= 6'b000010; // 20MHz long filters=0x00000000; // set HPF - if(ddsAFrequency<1800000L) { + if(ddsFrequency<1800000L) { filters|=ALEX_BYPASS_HPF; - } else if(ddsAFrequency<6500000L) { + } else if(ddsFrequency<6500000L) { filters|=ALEX_1_5MHZ_HPF; - } else if(ddsAFrequency<9500000L) { + } else if(ddsFrequency<9500000L) { filters|=ALEX_6_5MHZ_HPF; - } else if(ddsAFrequency<13000000L) { + } else if(ddsFrequency<13000000L) { filters|=ALEX_9_5MHZ_HPF; - } else if(ddsAFrequency<20000000L) { + } else if(ddsFrequency<20000000L) { filters|=ALEX_13MHZ_HPF; } else { filters|=ALEX_20MHZ_HPF; @@ -501,17 +261,17 @@ else if (frequency > 2400000) LPF <= 7'b0000100; // > 160m so use 80m LPF else LPF <= 7'b0001000; // < 2.4MHz so use 160m LPF^M */ - if(ddsAFrequency>32000000) { + if(ddsFrequency>32000000) { filters|=ALEX_6_BYPASS_LPF; - } else if(ddsAFrequency>22000000) { + } else if(ddsFrequency>22000000) { filters|=ALEX_12_10_LPF; - } else if(ddsAFrequency>15000000) { + } else if(ddsFrequency>15000000) { filters|=ALEX_17_15_LPF; - } else if(ddsAFrequency>8000000) { + } else if(ddsFrequency>8000000) { filters|=ALEX_30_20_LPF; - } else if(ddsAFrequency>4500000) { + } else if(ddsFrequency>4500000) { filters|=ALEX_60_40_LPF; - } else if(ddsAFrequency>2400000) { + } else if(ddsFrequency>2400000) { filters|=ALEX_80_LPF; } else { filters|=ALEX_160_LPF; @@ -588,7 +348,22 @@ static void new_protocol_transmit_specific() { buffer[10]=cw_keyer_weight; // cw weight buffer[11]=cw_keyer_hang_time>>8; buffer[12]=cw_keyer_hang_time; // cw hang delay buffer[13]=0; // rf delay - buffer[50]=orion_mic; + buffer[50]=0; + if(mic_linein) { + buffer[50]|=0x01; + } + if(mic_boost) { + buffer[50]|=0x02; + } + if(mic_ptt_enabled==0) { + buffer[50]|=0x04; + } + if(mic_bias_enabled) { + buffer[50]|=0x10; + } + if(mic_ptt_tip_bias_ring) { + buffer[50]|=0x08; + } buffer[51]=0x7F; // Line in gain if(sendto(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&transmitter_addr,transmitter_addr_length)<0) { @@ -629,12 +404,12 @@ static void new_protocol_receive_specific() { buffer[17+(i*6)]=adc[i]; } - buffer[18]=((sampleRate/1000)>>8)&0xFF; - buffer[19]=(sampleRate/1000)&0xFF; + buffer[18]=((sample_rate/1000)>>8)&0xFF; + buffer[19]=(sample_rate/1000)&0xFF; buffer[22]=24; - buffer[24]=((sampleRate/1000)>>8)&0xFF; - buffer[25]=(sampleRate/1000)&0xFF; + buffer[24]=((sample_rate/1000)>>8)&0xFF; + buffer[25]=(sample_rate/1000)&0xFF; buffer[28]=24; if(sendto(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&receiver_addr,receiver_addr_length)<0) { @@ -761,7 +536,7 @@ fprintf(stderr,"new_protocol_thread: receiver=%d\n", receiver); micsamples=0; iqindex=4; - switch(sampleRate) { + switch(sample_rate) { case 48000: outputsamples=BUFFER_SIZE; break; @@ -851,7 +626,7 @@ fprintf(stderr,"outputsamples=%d\n", outputsamples); //fprintf(stderr,"samples per frame %d\n",samplesperframe); - //if(!isTransmitting()) { + if(!isTransmitting()) { b=16; for(i=0;i #include #include diff --git a/old_discovery.c b/old_discovery.c new file mode 100644 index 0000000..5b9835b --- /dev/null +++ b/old_discovery.c @@ -0,0 +1,251 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "discovered.h" +#include "old_discovery.h" + +static char interface_name[64]; +static struct sockaddr_in interface_addr={0}; +static int interface_length; + +#define DISCOVERY_PORT 1024 +static int discovery_socket; +static struct sockaddr_in discovery_addr; + +static pthread_t discover_thread_id; +static void* discover_receive_thread(void* arg); + +static void discover(struct ifaddrs* iface) { + int rc; + struct sockaddr_in *sa; + //char *addr; + + strcpy(interface_name,iface->ifa_name); + fprintf(stderr,"discover: looking for HPSDR devices on %s\n",interface_name); + + // send a broadcast to locate hpsdr boards on the network + discovery_socket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); + if(discovery_socket<0) { + perror("discover: create socket failed for discovery_socket\n"); + exit(-1); + } + + int optval = 1; + setsockopt(discovery_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + sa = (struct sockaddr_in *) iface->ifa_addr; + //addr = inet_ntoa(sa->sin_addr); + + // bind to this interface and the discovery port + interface_addr.sin_family = AF_INET; + interface_addr.sin_addr.s_addr = sa->sin_addr.s_addr; + interface_addr.sin_port = htons(DISCOVERY_PORT*2); + if(bind(discovery_socket,(struct sockaddr*)&interface_addr,sizeof(interface_addr))<0) { + perror("discover: bind socket failed for discovery_socket\n"); + exit(-1); + } + + fprintf(stderr,"discover: bound to %s\n",interface_name); + + // allow broadcast on the socket + int on=1; + rc=setsockopt(discovery_socket, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + if(rc != 0) { + fprintf(stderr,"discover: cannot set SO_BROADCAST: rc=%d\n", rc); + exit(-1); + } + + // setup to address + struct sockaddr_in to_addr={0}; + to_addr.sin_family=AF_INET; + to_addr.sin_port=htons(DISCOVERY_PORT); + to_addr.sin_addr.s_addr=htonl(INADDR_BROADCAST); + + // start a receive thread to collect discovery response packets + rc=pthread_create(&discover_thread_id,NULL,discover_receive_thread,NULL); + if(rc != 0) { + fprintf(stderr,"pthread_create failed on discover_receive_thread: rc=%d\n", rc); + exit(-1); + } + + + // send discovery packet + unsigned char buffer[63]; + buffer[0]=0xEF; + buffer[1]=0xFE; + buffer[2]=0x02; + int i; + for(i=3;i<63;i++) { + buffer[i]=0x00; + } + + if(sendto(discovery_socket,buffer,63,0,(struct sockaddr*)&to_addr,sizeof(to_addr))<0) { + perror("discover: sendto socket failed for discovery_socket\n"); + exit(-1); + } + + // wait for receive thread to complete + void* status; + pthread_join(discover_thread_id,&status); + + close(discovery_socket); + + fprintf(stderr,"discover: exiting discover for %s\n",iface->ifa_name); + +} + +static void* discover_receive_thread(void* arg) { + struct sockaddr_in addr; + int len; + unsigned char buffer[2048]; + int bytes_read; + struct timeval tv; + int i; + +fprintf(stderr,"discover_receive_thread\n"); + + tv.tv_sec = 2; + tv.tv_usec = 0; + + setsockopt(discovery_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)); + + len=sizeof(addr); + while(1) { + bytes_read=recvfrom(discovery_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&addr,&len); + if(bytes_read<0) { + fprintf(stderr,"discovery: bytes read %d\n", bytes_read); + perror("discovery: recvfrom socket failed for discover_receive_thread"); + break; + } + fprintf(stderr,"discovered: received %d bytes\n",bytes_read); + if ((buffer[0] & 0xFF) == 0xEF && (buffer[1] & 0xFF) == 0xFE) { + int status = buffer[2] & 0xFF; + if (status == 2 || status == 3) { + if(devicesifa_addr && ifa->ifa_addr->sa_family == AF_INET) { + if((ifa->ifa_flags&IFF_UP)==IFF_UP + && (ifa->ifa_flags&IFF_RUNNING)==IFF_RUNNING + && (ifa->ifa_flags&IFF_LOOPBACK)!=IFF_LOOPBACK) { + discover(ifa); + } + } + ifa = ifa->ifa_next; + } + freeifaddrs(addrs); + + fprintf(stderr, "discovery found %d devices\n",devices); + + int i; + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "band.h" +#include "channel.h" +#include "discovered.h" +#include "mode.h" +#include "filter.h" +#include "old_protocol.h" +#include "radio.h" +#include "toolbar.h" + +#define PI 3.1415926535897932F + +#define DATA_PORT 1024 + +#define SYNC 0x7F +#define OZY_BUFFER_SIZE 512 +#define OUTPUT_BUFFER_SIZE 1024 + +// ozy command and control +#define MOX_DISABLED 0x00 +#define MOX_ENABLED 0x01 + +#define MIC_SOURCE_JANUS 0x00 +#define MIC_SOURCE_PENELOPE 0x80 +#define CONFIG_NONE 0x00 +#define CONFIG_PENELOPE 0x20 +#define CONFIG_MERCURY 0x40 +#define CONFIG_BOTH 0x60 +#define PENELOPE_122_88MHZ_SOURCE 0x00 +#define MERCURY_122_88MHZ_SOURCE 0x10 +#define ATLAS_10MHZ_SOURCE 0x00 +#define PENELOPE_10MHZ_SOURCE 0x04 +#define MERCURY_10MHZ_SOURCE 0x08 +#define SPEED_48K 0x00 +#define SPEED_96K 0x01 +#define SPEED_192K 0x02 +#define SPEED_384K 0x03 +#define MODE_CLASS_E 0x01 +#define MODE_OTHERS 0x00 +#define ALEX_ATTENUATION_0DB 0x00 +#define ALEX_ATTENUATION_10DB 0x01 +#define ALEX_ATTENUATION_20DB 0x02 +#define ALEX_ATTENUATION_30DB 0x03 +#define LT2208_GAIN_OFF 0x00 +#define LT2208_GAIN_ON 0x04 +#define LT2208_DITHER_OFF 0x00 +#define LT2208_DITHER_ON 0x08 +#define LT2208_RANDOM_OFF 0x00 +#define LT2208_RANDOM_ON 0x10 + +static int buffer_size=BUFFER_SIZE; + +static int receiver; +static int display_width; + +static int speed; + +static int dsp_rate=48000; +static int output_rate=48000; + +static int data_socket; +static struct sockaddr_in data_addr; +static int data_addr_length; + +static int output_buffer_size; + +static unsigned char control_in[5]={0x00,0x00,0x00,0x00,0x00}; +static unsigned char control_out[5]={0x00,0x00,0x00,0x00,0x00}; + +static double tuning_phase; +static float phase=0.0f; + +static int running; +static long ep4_sequence; + +static int samples=0; + +//static float left_input_buffer[BUFFER_SIZE]; +//static float right_input_buffer[BUFFER_SIZE]; +static double iqinputbuffer[BUFFER_SIZE*2]; + +//static float mic_left_buffer[BUFFER_SIZE]; +//static float mic_right_buffer[BUFFER_SIZE]; +static double micinputbuffer[BUFFER_SIZE*2]; + +//static float left_output_buffer[OUTPUT_BUFFER_SIZE]; +//static float right_output_buffer[OUTPUT_BUFFER_SIZE]; +static double audiooutputbuffer[BUFFER_SIZE*2]; + +//static float left_subrx_output_buffer[OUTPUT_BUFFER_SIZE]; +//static float right_subrx_output_buffer[OUTPUT_BUFFER_SIZE]; + +//static float left_tx_buffer[OUTPUT_BUFFER_SIZE]; +//static float right_tx_buffer[OUTPUT_BUFFER_SIZE]; +static double micoutputbuffer[BUFFER_SIZE*2]; + +static short left_rx_sample; +static short right_rx_sample; +static short left_tx_sample; +static short right_tx_sample; + +static unsigned char output_buffer[OZY_BUFFER_SIZE]; +static int output_buffer_index=0; + +static int command=0; + +static pthread_t receive_thread_id; +static void start_receive_thread(); +static void *receive_thread(void* arg); +static void process_ozy_input_buffer(char *buffer); +static void process_bandscope_buffer(char *buffer); +void ozy_send_buffer(); + +static unsigned char metis_buffer[1032]; +static long send_sequence=-1; +static int metis_offset=8; + +static int frequencyChanged=0; +static sem_t frequency_changed_sem; + +static int metis_write(unsigned char ep,char* buffer,int length); +static void metis_start_stop(int command); +static void metis_send_buffer(char* buffer,int length); + +void schedule_frequency_changed() { +//fprintf(stderr,"old_protocol: schedule_frequency_changed\n"); + //sem_wait(&frequency_changed_sem); + frequencyChanged=1; + //sem_post(&frequency_changed_sem); +} + +static float sineWave(double* buf, int samples, float phase, float freq) { + float phase_step = 2 * PI * freq / 192000.0F; + int i; + for (i = 0; i < samples; i++) { + buf[i*2] = (double) sin(phase); + phase += phase_step; + } + return phase; +} + +static void setSpeed(int s) { + int speed=SPEED_48K; + output_buffer_size=OUTPUT_BUFFER_SIZE; + switch(s) { + case 48000: + speed=SPEED_48K; + output_buffer_size=OUTPUT_BUFFER_SIZE; + break; + case 96000: + speed=SPEED_96K; + output_buffer_size=OUTPUT_BUFFER_SIZE/2; + break; + case 192000: + speed=SPEED_192K; + output_buffer_size=OUTPUT_BUFFER_SIZE/4; + break; + case 384000: + speed=SPEED_384K; + output_buffer_size=OUTPUT_BUFFER_SIZE/8; + break; + default: + fprintf(stderr,"Invalid sample rate: %d. Defaulting to 48K.\n",s); + break; + } + + //fprintf(stderr,"setSpeed sample_rate=%d speed=%d\n",s,speed); + + control_out[1]=control_out[1]&0xFC; + control_out[1]=control_out[1]|speed; + +} + + +void old_protocol_stop() { + metis_start_stop(0); + running=FALSE; +} + +void old_protocol_init(int rx,int pixels) { + int i; + + fprintf(stderr,"old_protocol_init\n"); + + //int result=sem_init(&frequency_changed_sem, 0, 1); + + receiver=rx; + display_width=pixels; + + // setup defaults + control_out[0] = MOX_DISABLED; + control_out[1] = CONFIG_BOTH + | MERCURY_122_88MHZ_SOURCE + | MERCURY_10MHZ_SOURCE + | speed + | MIC_SOURCE_PENELOPE; + control_out[2] = MODE_OTHERS; + control_out[3] = ALEX_ATTENUATION_0DB + | LT2208_GAIN_OFF + | LT2208_DITHER_ON + | LT2208_RANDOM_ON; + control_out[4] = 0; + + setSpeed(sample_rate); + + start_receive_thread(); + + fprintf(stderr,"old_protocol_init: prime radio\n"); + for(i=8;iinterface_address,d->interface_length)<0) { + perror("old_protocol: bind socket failed for data_socket\n"); + exit(-1); + } + + memcpy(&data_addr,&d->address,d->address_length); + data_addr_length=d->address_length; + data_addr.sin_port=htons(DATA_PORT); + + rc=pthread_create(&receive_thread_id,NULL,receive_thread,NULL); + if(rc != 0) { + fprintf(stderr,"old_protocol: pthread_create failed on receive_thread: rc=%d\n", rc); + exit(-1); + } + +} + +static void *receive_thread(void* arg) { + struct sockaddr_in addr; + int length; + unsigned char buffer[2048]; + int bytes_read; + int ep; + long sequence; + + fprintf(stderr, "old_protocol: receive_thread\n"); + running=1; + + length=sizeof(addr); + while(running) { + bytes_read=recvfrom(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&addr,&length); + if(bytes_read<0) { + perror("recvfrom socket failed for old_protocol: receive_thread"); + exit(1); + } + + if(buffer[0]==0xEF && buffer[1]==0xFE) { + switch(buffer[2]) { + case 1: + // get the end point + ep=buffer[3]&0xFF; + + // get the sequence number + sequence=((buffer[4]&0xFF)<<24)+((buffer[5]&0xFF)<<16)+((buffer[6]&0xFF)<<8)+(buffer[7]&0xFF); + + switch(ep) { + case 6: // EP6 + // process the data + process_ozy_input_buffer(&buffer[8]); + process_ozy_input_buffer(&buffer[520]); + break; + case 4: // EP4 +/* + ep4_sequence++; + if(sequence!=ep4_sequence) { + ep4_sequence=sequence; + } else { + int seq=(int)(sequence%32L); + if((sequence%32L)==0L) { + reset_bandscope_buffer_index(); + } + process_bandscope_buffer(&buffer[8]); + process_bandscope_buffer(&buffer[520]); + } +*/ + break; + default: + fprintf(stderr,"unexpected EP %d length=%d\n",ep,bytes_read); + break; + } + break; + case 2: // response to a discovery packet + fprintf(stderr,"unexepected discovery response when not in discovery mode\n"); + break; + default: + fprintf(stderr,"unexpected packet type: 0x%02X\n",buffer[2]); + break; + } + } else { + fprintf(stderr,"received bad header bytes on data port %02X,%02X\n",buffer[0],buffer[1]); + } + } +} + +static void process_ozy_input_buffer(char *buffer) { + int i,j; + int b=0; + unsigned char ozy_samples[8*8]; + int bytes; + int last_ptt; + int last_dot; + int last_dash; + double gain; + int left_sample; + int right_sample; + int mic_sample; + float left_sample_float; + float right_sample_float; + float mic_sample_float; + + if(buffer[b++]==SYNC && buffer[b++]==SYNC && buffer[b++]==SYNC) { + // extract control bytes + control_in[0]=buffer[b++]; + control_in[1]=buffer[b++]; + control_in[2]=buffer[b++]; + control_in[3]=buffer[b++]; + control_in[4]=buffer[b++]; + + last_ptt=ptt; + last_dot=dot; + last_dash=dash; + ptt=(control_in[0]&0x01)==0x01; + dash=(control_in[0]&0x02)==0x02; + dot=(control_in[0]&0x04)==0x04; + + if(last_ptt!=ptt) { + g_idle_add(ptt_update,(gpointer)ptt); + } + + switch((control_in[0]>>3)&0x1F) { + case 0: + adc_overload=control_in[1]&0x01; + IO1=(control_in[1]&0x02)?0:1; + IO2=(control_in[1]&0x04)?0:1; + IO3=(control_in[1]&0x08)?0:1; + if(mercury_software_version!=control_in[2]) { + mercury_software_version=control_in[2]; + fprintf(stderr," Mercury Software version: %d (0x%0X)\n",mercury_software_version,mercury_software_version); + } + if(penelope_software_version!=control_in[3]) { + penelope_software_version=control_in[3]; + fprintf(stderr," Penelope Software version: %d (0x%0X)\n",penelope_software_version,penelope_software_version); + } + if(ozy_software_version!=control_in[4]) { + ozy_software_version=control_in[4]; + fprintf(stderr,"FPGA firmware version: %d.%d\n",ozy_software_version/10,ozy_software_version%10); + } + break; + case 1: + exciter_power=((control_in[1]&0xFF)<<8)|(control_in[2]&0xFF); // from Penelope or Hermes + alex_forward_power=((control_in[3]&0xFF)<<8)|(control_in[4]&0xFF); // from Alex or Apollo + break; + case 2: + alex_reverse_power=((control_in[1]&0xFF)<<8)|(control_in[2]&0xFF); // from Alex or Apollo + AIN3=(control_in[3]<<8)+control_in[4]; // from Pennelope or Hermes + break; + case 3: + AIN4=(control_in[1]<<8)+control_in[2]; // from Pennelope or Hermes + AIN6=(control_in[3]<<8)+control_in[4]; // from Pennelope or Hermes + break; + } + + + // extract the 63 samples + for(i=0;i<63;i++) { + + left_sample = (int)((signed char) buffer[b++]) << 16; + left_sample += (int)((unsigned char)buffer[b++]) << 8; + left_sample += (int)((unsigned char)buffer[b++]); + right_sample = (int)((signed char) buffer[b++]) << 16; + right_sample += (int)((unsigned char)buffer[b++]) << 8; + right_sample += (int)((unsigned char)buffer[b++]); + mic_sample = (int)((signed char) buffer[b++]) << 8; + mic_sample += (int)((unsigned char)buffer[b++]); +/* + left_sample = ((int)buffer[b++]) << 16; + left_sample = (((unsigned char)buffer[b++]) << 8) | left_sample; + left_sample = ((unsigned char)buffer[b++]) | left_sample; + right_sample = ((int)buffer[b++]) << 16; + right_sample = (((unsigned char)buffer[b++]) << 8) | right_sample; + right_sample = ((unsigned char)buffer[b++]) | right_sample; + mic_sample = ((int)buffer[b++]) << 8; + mic_sample = ((unsigned char)buffer[b++]) | mic_sample; +*/ + left_sample_float=(float)left_sample/8388607.0; // 24 bit sample 2^23-1 + right_sample_float=(float)right_sample/8388607.0; // 24 bit sample 2^23-1 + mic_sample_float=(float)mic_sample/32767.0f; // 16 bit sample 2^16-1 + + // add to buffer + if(isTransmitting()) { + micinputbuffer[samples*2]=(double)mic_sample_float*mic_gain; + micinputbuffer[(samples*2)+1]=(double)mic_sample_float*mic_gain; + samples++; + } else { + iqinputbuffer[samples*2]=(double)left_sample_float; + iqinputbuffer[(samples*2)+1]=(double)right_sample_float; + samples++; + } + + // when we have enough samples give them to WDSP and get the results + if(samples==buffer_size) { + int error; + if(isTransmitting()) { + if(tune) { + double tunefrequency = (double)((filterHigh - filterLow) / 2); + phase=sineWave(micinputbuffer, BUFFER_SIZE, phase, (float)tunefrequency); + } else if(mode==modeCWU || mode==modeCWL) { + } + // process the output + fexchange0(CHANNEL_TX, micinputbuffer, micoutputbuffer, &error); + if(error!=0) { + fprintf(stderr,"fexchange0 (CHANNEL_TX) returned error: %d\n", error); + } + Spectrum0(1, CHANNEL_TX, 0, 0, micoutputbuffer); + if(penelope) { + if(tune) { + gain=65535.0*255.0/(double)tune_drive; + } else { + gain=65535.0*255.0/(double)drive; + } + } else { + gain=65535.0; + } + for(j=0;j>8; + output_buffer[output_buffer_index++]=left_rx_sample; + output_buffer[output_buffer_index++]=right_rx_sample>>8; + output_buffer[output_buffer_index++]=right_rx_sample; + output_buffer[output_buffer_index++]=left_tx_sample>>8; + output_buffer[output_buffer_index++]=left_tx_sample; + output_buffer[output_buffer_index++]=right_tx_sample>>8; + output_buffer[output_buffer_index++]=right_tx_sample; + if(output_buffer_index>=OZY_BUFFER_SIZE) { + ozy_send_buffer(); + output_buffer_index=8; + } + } + } else { + // process the input + fexchange0(CHANNEL_RX0, iqinputbuffer, audiooutputbuffer, &error); + if(error!=0) { + fprintf(stderr,"fexchange2 (CHANNEL_RX0) returned error: %d\n", error); + } + Spectrum0(1, CHANNEL_RX0, 0, 0, iqinputbuffer); + for(j=0;j>8; + output_buffer[output_buffer_index++]=left_rx_sample; + output_buffer[output_buffer_index++]=right_rx_sample>>8; + output_buffer[output_buffer_index++]=right_rx_sample; + output_buffer[output_buffer_index++]=left_tx_sample>>8; + output_buffer[output_buffer_index++]=left_tx_sample; + output_buffer[output_buffer_index++]=right_tx_sample>>8; + output_buffer[output_buffer_index++]=right_tx_sample; + if(output_buffer_index>=OZY_BUFFER_SIZE) { + ozy_send_buffer(); + output_buffer_index=8; + } + } + } + samples=0; + } + } + } else { + time_t t; + struct tm* gmt; + time(&t); + gmt=gmtime(&t); + + fprintf(stderr,"%s: process_ozy_input_buffer: did not find sync\n", + asctime(gmt)); + exit(1); + } +} + +/* +static void process_bandscope_buffer(char *buffer) { +} +*/ + + +void ozy_send_buffer() { + output_buffer[0]=SYNC; + output_buffer[1]=SYNC; + output_buffer[2]=SYNC; + + switch(command) { +#ifdef EXCLUDE + case 0: + //sem_wait(&frequency_changed_sem); + if(frequencyChanged) { + // send rx frequency + output_buffer[3]=control_out[0]|0x04; + output_buffer[4]=ddsFrequency>>24; + output_buffer[5]=ddsFrequency>>16; + output_buffer[6]=ddsFrequency>>8; + output_buffer[7]=ddsFrequency; + //freqcommand++; + } else { + output_buffer[3]=control_out[0]; + output_buffer[4]=control_out[1]; + output_buffer[5]=control_out[2]; + output_buffer[6]=control_out[3]; + output_buffer[7]=control_out[4]; + } + //sem_post(&frequency_changed_sem); + break; + case 1: + // send tx frequency + output_buffer[3]=control_out[0]|0x02; +/* + if(bSplit) { + if(frequencyBChanged) { + output_buffer[3]=control_out[0]|0x02; // Penelope + output_buffer[4]=ddsBFrequency>>24; + output_buffer[5]=ddsBFrequency>>16; + output_buffer[6]=ddsBFrequency>>8; + output_buffer[7]=ddsBFrequency; + } else { + output_buffer[3]=control_out[0]; + output_buffer[4]=control_out[1]; + output_buffer[5]=control_out[2]; + output_buffer[6]=control_out[3]; + output_buffer[7]=control_out[4]; + } + } else { +*/ + //sem_wait(&frequency_changed_sem); + if(frequencyChanged) { + output_buffer[4]=ddsFrequency>>24; + output_buffer[5]=ddsFrequency>>16; + output_buffer[6]=ddsFrequency>>8; + output_buffer[7]=ddsFrequency; + } else { + output_buffer[3]=control_out[0]; + output_buffer[4]=control_out[1]; + output_buffer[5]=control_out[2]; + output_buffer[6]=control_out[3]; + output_buffer[7]=control_out[4]; + } + frequencyChanged=0; + //sem_post(&frequency_changed_sem); +/* + } +*/ +/* + frequencyBChanged=0; +*/ + break; + case 2: + output_buffer[3]=control_out[0]; + output_buffer[4]=control_out[1]; + output_buffer[5]=control_out[2]; + output_buffer[6]=control_out[3]; + output_buffer[7]=control_out[4]; + break; +#endif + + case 0: + output_buffer[3]=control_out[0]; + output_buffer[4]=control_out[1]; + output_buffer[5]=control_out[2]; + output_buffer[6]=control_out[3]; + output_buffer[7]=control_out[4]; + break; + case 1: + output_buffer[3]=control_out[0]|0x04; + output_buffer[4]=ddsFrequency>>24; + output_buffer[5]=ddsFrequency>>16; + output_buffer[6]=ddsFrequency>>8; + output_buffer[7]=ddsFrequency; + break; + case 2: + output_buffer[3]=control_out[0]|0x02; + output_buffer[4]=ddsFrequency>>24; + output_buffer[5]=ddsFrequency>>16; + output_buffer[6]=ddsFrequency>>8; + output_buffer[7]=ddsFrequency; + break; + case 3: + { + float d=(float)drive; + if(tune) { + d=(float)tune_drive; + } + BAND *band=band_get_current_band(); + d=(d/100.0F)*(float)band->pa_calibration; + + output_buffer[3]=0x12; + output_buffer[4]=(int)d; + output_buffer[5]=control_out[2]; + if(mic_boost) { + output_buffer[5]|=0x01; + } + if(mic_linein) { + output_buffer[5]|=0x02; + } + if(filter_board==APOLLO) { + output_buffer[5]|=0x2C; // board, filter ,tuner + } + if((filter_board==APOLLO) && tune && apollo_tuner) { + output_buffer[5]|=0x10; + } + output_buffer[6]=control_out[3]; + output_buffer[7]=control_out[4]; + } + break; + case 4: + // need to add orion tip/ring and bias configuration + output_buffer[3]=0x14; + output_buffer[4]=0x00; + if(mic_ptt_enabled==0) { + output_buffer[4]|=0x40; + } + if(mic_bias_enabled) { + output_buffer[4]|=0x20; + } + if(mic_ptt_tip_bias_ring) { + output_buffer[4]|=0x10; + } + output_buffer[5]=0x00; + output_buffer[6]=0x00; + output_buffer[7]=0x00; + break; + case 5: + // need to add rx attenuation and cw configuration + output_buffer[3]=0x16; + output_buffer[4]=0x00; + output_buffer[5]=0x00; + if(cw_keys_reversed!=0) { + output_buffer[5]|=0x40; + } + output_buffer[6]=cw_keyer_speed | (cw_keyer_mode<<6); + output_buffer[7]=cw_keyer_weight | (cw_keyer_spacing<<7); + break; + case 6: + // need to add tx attenuation and rx ADC selection + output_buffer[3]=0x1C; + output_buffer[4]=0x00; + output_buffer[5]=0x00; + output_buffer[6]=0x00; + output_buffer[7]=0x00; + break; + case 7: + // need to add cw configuration + output_buffer[3]=0x1E; + if(cw_keyer_internal==1) { + if(isTransmitting() || (mode!=modeCWU && mode!=modeCWL)) { + output_buffer[4]=0x00; + } else { + output_buffer[4]=0x01; + } + } else { + output_buffer[4]=0x00; + } + output_buffer[5]=cw_keyer_sidetone_volume; + output_buffer[6]=cw_keyer_ptt_delay; + output_buffer[7]=0x00; + break; + case 8: + // need to add cw configuration + output_buffer[3]=0x20; + output_buffer[4]=cw_keyer_hang_time; + output_buffer[5]=cw_keyer_hang_time>>8; + output_buffer[6]=cw_keyer_sidetone_frequency; + output_buffer[7]=cw_keyer_sidetone_frequency>>8; + break; + } + command++; + //if(command>=14) { + if(command>=8) { + command=0; + } + // set mox + output_buffer[3]|=isTransmitting(); + + metis_write(0x02,output_buffer,OZY_BUFFER_SIZE); +} + +static int metis_write(unsigned char ep,char* buffer,int length) { + int i; + + // copy the buffer over + for(i=0;i<512;i++) { + metis_buffer[i+metis_offset]=buffer[i]; + } + + if(metis_offset==8) { + metis_offset=520; + } else { + send_sequence++; + metis_buffer[0]=0xEF; + metis_buffer[1]=0xFE; + metis_buffer[2]=0x01; + metis_buffer[3]=ep; + metis_buffer[4]=(send_sequence>>24)&0xFF; + metis_buffer[5]=(send_sequence>>16)&0xFF; + metis_buffer[6]=(send_sequence>>8)&0xFF; + metis_buffer[7]=(send_sequence)&0xFF; + + // send the buffer + metis_send_buffer(&metis_buffer[0],1032); + metis_offset=8; + + } + + return length; +} + +static void metis_start_stop(int command) { + int i; + unsigned char buffer[64]; + + buffer[0]=0xEF; + buffer[1]=0xFE; + buffer[2]=0x04; // start/stop command + buffer[3]=command; // send EP6 and EP4 data (0x00=stop) + + for(i=0;i<60;i++) { + buffer[i+4]=0x00; + } + + metis_send_buffer(buffer,sizeof(buffer)); +} + +static void metis_send_buffer(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/old_protocol.h b/old_protocol.h new file mode 100644 index 0000000..633006b --- /dev/null +++ b/old_protocol.h @@ -0,0 +1,23 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +#define BUFFER_SIZE 1024 +void old_protocol_stop(); +void old_protocol_init(int rx,int pixels); +void schedule_frequency_changed(); diff --git a/panadapter.c b/panadapter.c index 96284ef..4d50c37 100644 --- a/panadapter.c +++ b/panadapter.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #include #include @@ -5,7 +24,9 @@ #include #include #include -#include "new_protocol.h" +#include "band.h" +#include "discovered.h" +#include "radio.h" #include "panadapter.h" #include "vfo.h" @@ -18,8 +39,8 @@ static gint last_x; static gboolean has_moved=FALSE; static gboolean pressed=FALSE; -static float panadapter_max=-60.0; -static float panadapter_min=-160.0; +//static float panadapter_max=-60.0; +//static float panadapter_min=-160.0; static gfloat hz_per_pixel; static gfloat filter_left; @@ -155,13 +176,17 @@ void panadapter_update(float *data,int tx) { if(panadapter_surface) { if(tx) { - saved_max=panadapter_max; - saved_min=panadapter_min; + saved_max=panadapter_high; + saved_min=panadapter_low; saved_hz_per_pixel=hz_per_pixel; - panadapter_max=30; - panadapter_min=-100; - hz_per_pixel=192000.0/(double)display_width; + panadapter_high=20; + panadapter_low=-100; + if(protocol==ORIGINAL_PROTOCOL) { + hz_per_pixel=48000.0/(double)display_width; + } else { + hz_per_pixel=192000.0/(double)display_width; + } } //clear_panadater_surface(); @@ -178,11 +203,11 @@ void panadapter_update(float *data,int tx) { cairo_fill(cr); // plot the levels - int V = (int)(panadapter_max - panadapter_min); + int V = (int)(panadapter_high - panadapter_low); int numSteps = V / 20; for (i = 1; i < numSteps; i++) { - int num = panadapter_max - i * 20; - int y = (int)floor((panadapter_max - num) * panadapter_height / V); + int num = panadapter_high - i * 20; + int y = (int)floor((panadapter_high - num) * panadapter_height / V); cairo_set_source_rgb (cr, 0, 1, 1); cairo_set_line_width(cr, 1.0); @@ -203,8 +228,9 @@ void panadapter_update(float *data,int tx) { // plot frequency markers long f; + long half=(long)getSampleRate()/2L; for(i=0;i 0) { if ((f % 20000) < (long) hz_per_pixel) { cairo_set_source_rgb (cr, 0, 1, 1); @@ -226,6 +252,25 @@ void panadapter_update(float *data,int tx) { } cairo_stroke(cr); + // band edges + long min_display=getFrequency()-half; + long max_display=getFrequency()+half; + BAND_LIMITS* bandLimits=getBandLimits(min_display,max_display); + if(bandLimits!=NULL) { + cairo_set_source_rgb (cr, 1, 0, 0); + cairo_set_line_width(cr, 2.0); + if((min_displayminFrequency)&&(max_display>bandLimits->minFrequency)) { + i=(bandLimits->minFrequency-min_display)/(long long)hz_per_pixel; + cairo_move_to(cr,(double)i,0.0); + cairo_line_to(cr,(double)i,(double)panadapter_height); + } + if((min_displaymaxFrequency)&&(max_display>bandLimits->maxFrequency)) { + i=(bandLimits->maxFrequency-min_display)/(long long)hz_per_pixel; + cairo_move_to(cr,(double)i,0.0); + cairo_line_to(cr,(double)i,(double)panadapter_height); + } + } + // cursor cairo_set_source_rgb (cr, 1, 0, 0); cairo_set_line_width(cr, 1.0); @@ -238,17 +283,17 @@ void panadapter_update(float *data,int tx) { cairo_set_line_width(cr, 1.0); double s1,s2; - samples[0]=panadapter_min-20; - samples[display_width-1]=panadapter_min-20; + samples[0]=panadapter_low-20; + samples[display_width-1]=panadapter_low-20; for(i=1;ivKR)YLUoi+dIHUPM4ZMR7%D<*GH8 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 #include #include diff --git a/property.h b/property.h index 55cd608..5056c6e 100644 --- a/property.h +++ b/property.h @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + typedef struct _PROPERTY PROPERTY; /* --------------------------------------------------------------------------*/ diff --git a/radio.c b/radio.c index 1188605..3dcff68 100644 --- a/radio.c +++ b/radio.c @@ -1,31 +1,66 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #include #include +#include #include "radio.h" #include "agc.h" #include "band.h" #include "discovered.h" #include "property.h" +#include "new_protocol.h" char property_path[128]; +sem_t property_sem; + + +int penelope=0; +int tx_out_of_band=0; int sample_rate=48000; int filter_board=ALEX; int pa=PA_ENABLED; int apollo_tuner=0; -int panadapter_high=-80; -int panadapter_low=-160; +int display_panadapter=1; +int panadapter_high=-60; +int panadapter_low=-140; +int display_waterfall=1; int waterfall_high=-100; int waterfall_low=-150; int waterfall_automatic=1; +int display_sliders=1; +int display_toolbar=1; + double volume=0.2; double mic_gain=1.5; -int orion_mic=MIC_BOOST|ORION_MIC_PTT_ENABLED|ORION_MIC_PTT_TIP_BIAS_RING|ORION_MIC_BIAS_ENABLED; +int mic_linein=0; +int mic_boost=0; +int mic_bias_enabled=0; +int mic_ptt_enabled=0; +int mic_ptt_tip_bias_ring=0; int agc=AGC_MEDIUM; double agc_gain=60.0; @@ -49,6 +84,13 @@ int step=100; int byte_swap=0; +int lt2208Dither = 0; +int lt2208Random = 0; +int attenuation = 20; // 20dB +unsigned long alex_rx_antenna=0; +unsigned long alex_tx_antenna=0; +unsigned long alex_attenuation=0; + int cw_keys_reversed=0; // 0=disabled 1=enabled int cw_keyer_speed=12; // 1-60 WPM int cw_keyer_mode=KEYER_STRAIGHT; @@ -61,8 +103,161 @@ int cw_keyer_hang_time=300; // ms int cw_keyer_sidetone_frequency=400; // Hz int cw_breakin=1; // 0=disabled 1=enabled +int vfo_encoder_divisor=25; + +int protocol; +int device; +int ozy_software_version; +int mercury_software_version; +int penelope_software_version; +int ptt; +int dot; +int dash; +int adc_overload; +int pll_locked; +unsigned int exciter_power; +unsigned int alex_forward_power; +unsigned int alex_reverse_power; +unsigned int AIN3; +unsigned int AIN4; +unsigned int AIN6; +unsigned int IO1; +unsigned int IO2; +unsigned int IO3; +int supply_volts; +int mox; +int tune; + +long long ddsFrequency=14250000; + +void init_radio() { + int rc; + rc=sem_init(&property_sem, 0, 0); + if(rc!=0) { + fprintf(stderr,"init_radio: sem_init failed for property_sem: %d\n", rc); + exit(-1); + } + sem_post(&property_sem); +} + +void setSampleRate(int rate) { + sample_rate=rate; +} + +int getSampleRate() { + return sample_rate; +} + +void setMox(int state) { +fprintf(stderr,"setMox: protocol=%d\n", protocol); + if(mox!=state) { + mox=state; + if(protocol==NEW_PROTOCOL) { + schedule_high_priority(3); + } + } +} + +int getMox() { + return mox; +} + +void setTune(int state) { +fprintf(stderr,"setTune: protocol=%d\n", protocol); + if(tune!=state) { + tune=state; + if(protocol==NEW_PROTOCOL) { + schedule_high_priority(4); + schedule_general(); + } + } +} + +int getTune() { + return tune; +} + +int isTransmitting() { + return ptt!=0 || mox!=0 || tune!=0; +} + +void setFrequency(long long f) { +//fprintf(stderr,"setFrequency: protocol=%d f=%lld\n", protocol, f); + ddsFrequency=f; + if(protocol==NEW_PROTOCOL) { + schedule_high_priority(5); + } else { + schedule_frequency_changed(); + } +} + +long long getFrequency() { + return ddsFrequency; +} + +double getDrive() { + return (double)drive/255.0; +} + +void setDrive(double value) { +//fprintf(stderr,"setDrive: protocol=%d\n", protocol); + drive=(int)(value*255.0); + if(protocol==NEW_PROTOCOL) { + schedule_high_priority(6); + } +} + +double getTuneDrive() { + return (double)tune_drive/255.0; +} + +void setTuneDrive(double value) { +fprintf(stderr,"setTuneDrive: protocol=%d\n", protocol); + tune_drive=(int)(value*255.0); + if(protocol==NEW_PROTOCOL) { + schedule_high_priority(7); + } +} + +void set_attenuation(int value) { + attenuation=value; +} + +int get_attenuation() { + return attenuation; +} + +void set_alex_rx_antenna(unsigned long v) { + alex_rx_antenna=v; + if(protocol==NEW_PROTOCOL) { + schedule_high_priority(1); + } +} + +void set_alex_tx_antenna(unsigned long v) { + alex_tx_antenna=v; + if(protocol==NEW_PROTOCOL) { + schedule_high_priority(2); + } +} + +void set_alex_attenuation(unsigned long v) { + alex_attenuation=v; + if(protocol==NEW_PROTOCOL) { + schedule_high_priority(0); + } +} + void radioRestoreState() { char *value; + + sem_wait(&property_sem); + loadProperties(property_path); + + value=getProperty("penelope"); + if(value) penelope=atoi(value); + value=getProperty("tx_out_of_band"); + if(value) tx_out_of_band=atoi(value); value=getProperty("sample_rate"); if(value) sample_rate=atoi(value); value=getProperty("filter_board"); @@ -71,10 +266,18 @@ void radioRestoreState() { if(value) apollo_tuner=atoi(value); value=getProperty("pa"); if(value) pa=atoi(value); + value=getProperty("display_panadapter"); + if(value) display_panadapter=atoi(value); value=getProperty("panadapter_high"); if(value) panadapter_high=atoi(value); value=getProperty("panadapter_low"); if(value) panadapter_low=atoi(value); + value=getProperty("display_waterfall"); + if(value) display_waterfall=atoi(value); + value=getProperty("display_sliders"); + if(value) display_sliders=atoi(value); + value=getProperty("display_toolbar"); + if(value) display_toolbar=atoi(value); value=getProperty("waterfall_high"); if(value) waterfall_high=atoi(value); value=getProperty("waterfall_low"); @@ -83,10 +286,22 @@ void radioRestoreState() { if(value) waterfall_automatic=atoi(value); value=getProperty("volume"); if(value) volume=atof(value); + value=getProperty("drive"); + if(value) drive=atoi(value); + value=getProperty("tune_drive"); + if(value) tune_drive=atoi(value); value=getProperty("mic_gain"); if(value) mic_gain=atof(value); - value=getProperty("orion_mic"); - if(value) orion_mic=atof(value); + value=getProperty("mic_boost"); + if(value) mic_boost=atof(value); + value=getProperty("mic_linein"); + if(value) mic_linein=atof(value); + value=getProperty("mic_ptt_enabled"); + if(value) mic_ptt_enabled=atof(value); + value=getProperty("mic_bias_enabled"); + if(value) mic_bias_enabled=atof(value); + value=getProperty("mic_ptt_tip_bias_ring"); + if(value) mic_ptt_tip_bias_ring=atof(value); value=getProperty("nr"); if(value) nr=atoi(value); value=getProperty("nb"); @@ -125,13 +340,17 @@ void radioRestoreState() { if(value) cw_keyer_sidetone_frequency=atoi(value); value=getProperty("cw_breakin"); if(value) cw_breakin=atoi(value); - + value=getProperty("vfo_encoder_divisor"); + if(value) vfo_encoder_divisor=atoi(value); bandRestoreState(); + sem_post(&property_sem); } void radioSaveState() { char value[80]; + + sem_wait(&property_sem); sprintf(value,"%d",sample_rate); setProperty("sample_rate",value); sprintf(value,"%d",filter_board); @@ -140,10 +359,18 @@ void radioSaveState() { setProperty("apollo_tuner",value); sprintf(value,"%d",pa); setProperty("pa",value); + sprintf(value,"%d",display_panadapter); + setProperty("display_panadapter",value); sprintf(value,"%d",panadapter_high); setProperty("panadapter_high",value); sprintf(value,"%d",panadapter_low); setProperty("panadapter_low",value); + sprintf(value,"%d",display_waterfall); + setProperty("display_waterfall",value); + sprintf(value,"%d",display_sliders); + setProperty("display_sliders",value); + sprintf(value,"%d",display_toolbar); + setProperty("display_toolbar",value); sprintf(value,"%d",waterfall_high); setProperty("waterfall_high",value); sprintf(value,"%d",waterfall_low); @@ -154,8 +381,20 @@ void radioSaveState() { setProperty("volume",value); sprintf(value,"%f",mic_gain); setProperty("mic_gain",value); - sprintf(value,"%d",orion_mic); - setProperty("orion_mic",value); + sprintf(value,"%d",drive); + setProperty("drive",value); + sprintf(value,"%d",tune_drive); + setProperty("tune_drive",value); + sprintf(value,"%d",mic_boost); + setProperty("mic_boost",value); + sprintf(value,"%d",mic_linein); + setProperty("mic_linein",value); + sprintf(value,"%d",mic_ptt_enabled); + setProperty("mic_ptt_enabled",value); + sprintf(value,"%d",mic_bias_enabled); + setProperty("mic_bias_enabled",value); + sprintf(value,"%d",mic_ptt_tip_bias_ring); + setProperty("mic_ptt_tip_bias_ring",value); sprintf(value,"%d",nr); setProperty("nr",value); sprintf(value,"%d",nb); @@ -194,6 +433,11 @@ void radioSaveState() { setProperty("cw_keyer_sidetone_frequency",value); sprintf(value,"%d",cw_breakin); setProperty("cw_breakin",value); + sprintf(value,"%d",vfo_encoder_divisor); + setProperty("vfo_encoder_divisor",value); bandSaveState(); + + saveProperties(property_path); + sem_post(&property_sem); } diff --git a/radio.h b/radio.h index 28ef822..87d1b69 100644 --- a/radio.h +++ b/radio.h @@ -1,13 +1,43 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + + +#define NEW_MIC_IN 0x00 +#define NEW_LINE_IN 0x01 +#define NEW_MIC_BOOST 0x02 +#define NEW_ORION_MIC_PTT_ENABLED 0x00 +#define NEW_ORION_MIC_PTT_DISABLED 0x04 +#define NEW_ORION_MIC_PTT_RING_BIAS_TIP 0x00 +#define NEW_ORION_MIC_PTT_TIP_BIAS_RING 0x08 +#define NEW_ORION_MIC_BIAS_DISABLED 0x00 +#define NEW_ORION_MIC_BIAS_ENABLED 0x10 + +#define OLD_MIC_IN 0x00 +#define OLD_LINE_IN 0x02 +#define OLD_MIC_BOOST 0x01 +#define OLD_ORION_MIC_PTT_ENABLED 0x40 +#define OLD_ORION_MIC_PTT_DISABLED 0x00 +#define OLD_ORION_MIC_PTT_RING_BIAS_TIP 0x00 +#define OLD_ORION_MIC_PTT_TIP_BIAS_RING 0x08 +#define OLD_ORION_MIC_BIAS_DISABLED 0x00 +#define OLD_ORION_MIC_BIAS_ENABLED 0x20 -#define MIC_IN 0x00 -#define LINE_IN 0x01 -#define MIC_BOOST 0x02 -#define ORION_MIC_PTT_ENABLED 0x00 -#define ORION_MIC_PTT_DISABLED 0x04 -#define ORION_MIC_PTT_RING_BIAS_TIP 0x00 -#define ORION_MIC_PTT_TIP_BIAS_RING 0x08 -#define ORION_MIC_BIAS_DISABLED 0x00 -#define ORION_MIC_BIAS_ENABLED 0x10 extern char property_path[]; @@ -25,18 +55,26 @@ extern char property_path[]; #define KEYER_MODE_A 1 #define KEYER_MODE_B 2 +extern int penelope; +extern int tx_out_of_band; + extern int sample_rate; extern int filter_board; extern int pa; extern int apollo_tuner; +extern int display_panadapter; extern int panadapter_high; extern int panadapter_low; +extern int display_waterfall; extern int waterfall_high; extern int waterfall_low; extern int waterfall_automatic; +extern int display_sliders; +extern int display_toolbar; + extern double volume; extern double mic_gain; extern int agc; @@ -49,7 +87,11 @@ extern int snb; extern int cwPitch; -extern int orion_mic; +extern int mic_linein; +extern int mic_boost; +extern int mic_bias_enabled; +extern int mic_ptt_enabled; +extern int mic_ptt_tip_bias_ring; extern int tune_drive; extern int drive; @@ -63,6 +105,13 @@ extern int step; extern int byte_swap; +extern int lt2208Dither; +extern int lt2208Random; +extern int attenuation; +extern unsigned long alex_rx_antenna; +extern unsigned long alex_tx_antenna; +extern unsigned long alex_attenuation; + extern int cw_keys_reversed; extern int cw_keyer_speed; extern int cw_keyer_mode; @@ -75,6 +124,56 @@ extern int cw_keyer_hang_time; extern int cw_keyer_sidetone_frequency; extern int cw_breakin; +extern int vfo_encoder_divisor; + +extern int protocol; +extern int device; +extern int ozy_software_version; +extern int mercury_software_version; +extern int penelope_software_version; +extern int mox; +extern int tune; +extern int ptt; +extern int dot; +extern int dash; +extern int adc_overload; +extern int pll_locked; +extern unsigned int exciter_power; +extern unsigned int alex_forward_power; +extern unsigned int alex_reverse_power; +extern unsigned int IO1; +extern unsigned int IO2; +extern unsigned int IO3; +extern unsigned int AIN3; +extern unsigned int AIN4; +extern unsigned int AIN6; +extern int supply_volts; + +extern long long ddsFrequency; + +extern void init_radio(); +extern void setSampleRate(int rate); +extern int getSampleRate(); +extern void setMox(int state); +extern int getMox(); +extern void setTune(int state); +extern int getTune(); +double getDrive(); +void setDrive(double d); +double getTuneDrive(); +void setTuneDrive(double d); + +void set_attenuation(int value); +int get_attenuation(); +void set_alex_rx_antenna(unsigned long v); +void set_alex_tx_antenna(unsigned long v); +void set_alex_attenuation(unsigned long v); + +extern int isTransmitting(); + +extern void setFrequency(long long f); +extern long long getFrequency(); + extern void radioRestoreState(); extern void radioSaveState(); diff --git a/rotary_encoder.c b/rotary_encoder.c deleted file mode 100644 index 9e70db4..0000000 --- a/rotary_encoder.c +++ /dev/null @@ -1,298 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rotary_encoder.h" -#include "main.h" - -#define SYSFS_GPIO_DIR "/sys/class/gpio" - -#define RASPBERRYPI_VFO_ENCODER_A 14 -#define RASPBERRYPI_VFO_ENCODER_B 15 - -#define ODROID_VFO_ENCODER_A 108 -#define ODROID_VFO_ENCODER_B 97 -#define ODROID_VFO_ENCODER_A_PIN 23 -#define ODROID_VFO_ENCODER_B_PIN 24 - -#define AF_ENCODER_A 20 -#define AF_ENCODER_B 26 -#define AF_FUNCTION 21 - -#define RF_ENCODER_A 16 -#define RF_ENCODER_B 19 -#define RF_FUNCTION 13 - -#define FUNCTION 12 -#define BAND 6 - -static int VFO_ENCODER_A=14; -static int VFO_ENCODER_B=15; - -static int VFO_ENCODER_A_PIN=0; -static int VFO_ENCODER_B_PIN=0; - -static volatile int vfoEncoderPos; -static volatile int afEncoderPos; -static volatile int afFunction; -static volatile int rfEncoderPos; -static volatile int rfFunction; -static volatile int function; -static volatile int band; - -static void afFunctionAlert(int gpio, int level, uint32_t tick) { - afFunction=(level==0); -} - -static void rfFunctionAlert(int gpio, int level, uint32_t tick) { - rfFunction=(level==0); -} - -static void functionAlert(int gpio, int level, uint32_t tick) { - function=(level==0); -} - -static void bandAlert(int gpio, int level, uint32_t tick) { - band=(level==0); -} - -static void vfoEncoderPulse(int gpio, int level, unsigned int tick) { - static int levA=0, levB=0, lastGpio = -1; - -//fprintf(stderr,"vfoEncoderPulse:%d=%d\n",gpio,level); - if (gpio == VFO_ENCODER_A) levA = level; else levB = level; - - if (gpio != lastGpio) /* debounce */ - { - lastGpio = gpio; - - if ((gpio == VFO_ENCODER_A) && (level == 0)) - { - if (!levB) ++vfoEncoderPos; - } - else if ((gpio == VFO_ENCODER_B) && (level == 1)) - { - if (levA) --vfoEncoderPos; - } - } -} - -static void afEncoderPulse(int gpio, int level, uint32_t tick) -{ - static int levA=0, levB=0, lastGpio = -1; - - if (gpio == AF_ENCODER_A) levA = level; else levB = level; - - if (gpio != lastGpio) /* debounce */ - { - lastGpio = gpio; - - if ((gpio == AF_ENCODER_A) && (level == 0)) - { - if (!levB) ++afEncoderPos; - } - else if ((gpio == AF_ENCODER_B) && (level == 1)) - { - if (levA) --afEncoderPos; - } - } -} - -static void rfEncoderPulse(int gpio, int level, uint32_t tick) -{ - static int levA=0, levB=0, lastGpio = -1; - - if (gpio == RF_ENCODER_A) levA = level; else levB = level; - - if (gpio != lastGpio) /* debounce */ - { - lastGpio = gpio; - - if ((gpio == RF_ENCODER_A) && (level == 0)) - { - if (!levB) ++rfEncoderPos; - } - else if ((gpio == RF_ENCODER_B) && (level == 1)) - { - if (levA) --rfEncoderPos; - } - } -} - -void interruptB(void) { - vfoEncoderPulse(VFO_ENCODER_B,digitalRead(VFO_ENCODER_B_PIN),0); -} - -void interruptA(void) { - vfoEncoderPulse(VFO_ENCODER_A,digitalRead(VFO_ENCODER_A_PIN),0); -} - -int encoder_init() { - - if(strcmp(unameData.nodename,"raspberrypi")==0) { - - VFO_ENCODER_A=RASPBERRYPI_VFO_ENCODER_A; - VFO_ENCODER_B=RASPBERRYPI_VFO_ENCODER_B; - VFO_ENCODER_A_PIN=0; - VFO_ENCODER_B_PIN=0; - - fprintf(stderr,"encoder_init: VFO_ENCODER_A=%d VFO_ENCODER_B=%d\n",VFO_ENCODER_A,VFO_ENCODER_B); - - if(gpioInitialise()<0) { - fprintf(stderr,"Cannot initialize GPIO\n"); - return -1; - } - - gpioSetMode(VFO_ENCODER_A, PI_INPUT); - gpioSetMode(VFO_ENCODER_B, PI_INPUT); - gpioSetPullUpDown(VFO_ENCODER_A, PI_PUD_UP); - gpioSetPullUpDown(VFO_ENCODER_B, PI_PUD_UP); - gpioSetAlertFunc(VFO_ENCODER_A, vfoEncoderPulse); - gpioSetAlertFunc(VFO_ENCODER_B, vfoEncoderPulse); - vfoEncoderPos=0; - - - gpioSetMode(AF_FUNCTION, PI_INPUT); - gpioSetPullUpDown(AF_FUNCTION,PI_PUD_UP); - gpioSetAlertFunc(AF_FUNCTION, afFunctionAlert); - afFunction=0; - - gpioSetMode(AF_ENCODER_A, PI_INPUT); - gpioSetMode(AF_ENCODER_B, PI_INPUT); - gpioSetPullUpDown(AF_ENCODER_A, PI_PUD_OFF); - gpioSetPullUpDown(AF_ENCODER_B, PI_PUD_OFF); - gpioSetAlertFunc(AF_ENCODER_A, afEncoderPulse); - gpioSetAlertFunc(AF_ENCODER_B, afEncoderPulse); - afEncoderPos=0; - - gpioSetMode(RF_FUNCTION, PI_INPUT); - gpioSetPullUpDown(RF_FUNCTION,PI_PUD_UP); - gpioSetAlertFunc(RF_FUNCTION, rfFunctionAlert); - rfFunction=0; - - gpioSetMode(RF_ENCODER_A, PI_INPUT); - gpioSetMode(RF_ENCODER_B, PI_INPUT); - gpioSetPullUpDown(RF_ENCODER_A, PI_PUD_OFF); - gpioSetPullUpDown(RF_ENCODER_B, PI_PUD_OFF); - gpioSetAlertFunc(RF_ENCODER_A, rfEncoderPulse); - gpioSetAlertFunc(RF_ENCODER_B, rfEncoderPulse); - rfEncoderPos=0; - - gpioSetMode(FUNCTION, PI_INPUT); - gpioSetPullUpDown(FUNCTION,PI_PUD_UP); - gpioSetAlertFunc(FUNCTION, functionAlert); - - gpioSetMode(BAND, PI_INPUT); - gpioSetPullUpDown(BAND,PI_PUD_UP); - gpioSetAlertFunc(BAND, bandAlert); - - } else if(strcmp(unameData.nodename,"odroid")==0) { - - VFO_ENCODER_A=ODROID_VFO_ENCODER_A; - VFO_ENCODER_B=ODROID_VFO_ENCODER_B; - VFO_ENCODER_A_PIN=ODROID_VFO_ENCODER_A_PIN; - VFO_ENCODER_B_PIN=ODROID_VFO_ENCODER_B_PIN; - - fprintf(stderr,"encoder_init: VFO_ENCODER_A=%d VFO_ENCODER_B=%d\n",VFO_ENCODER_A,VFO_ENCODER_B); - - if (wiringPiSetup () < 0) { - printf ("Unable to setup wiringPi: %s\n", strerror (errno)); - return 1; - } - - FILE *fp; - - fp = popen("echo 97 > /sys/class/gpio/export\n", "r"); - pclose(fp); - fp = popen("echo \"in\" > /sys/class/gpio/gpio97/direction\n", "r"); - pclose(fp); - fp = popen("chmod 0666 /sys/class/gpio/gpio97/value\n", "r"); - pclose(fp); - - fp = popen("echo 108 > /sys/class/gpio/export\n", "r"); - pclose(fp); - fp = popen("echo \"in\" > /sys/class/gpio/gpio108/direction\n", "r"); - pclose(fp); - fp = popen("chmod 0666 /sys/class/gpio/gpio108/value\n", "r"); - pclose(fp); - - if ( wiringPiISR (24, INT_EDGE_BOTH, &interruptB) < 0 ) { - printf ("Unable to setup ISR: %s\n", strerror (errno)); - return 1; - } - - if ( wiringPiISR (23, INT_EDGE_BOTH, &interruptA) < 0 ) { - printf ("Unable to setup ISR: %s\n", strerror (errno)); - return 1; - } - } else { - fprintf(stderr,"Unknown nodename: %s. Rotary Encoder not enabled.\n",unameData.nodename); - return 1; - } - - - return 0; -} - -void encoder_close() { - if(strcmp(unameData.nodename,"odroid")==0) { - FILE *fp; - fp = popen("echo 97 > /sys/class/gpio/unexport\n", "r"); - pclose(fp); - fp = popen("echo 108 > /sys/class/gpio/unexport\n", "r"); - pclose(fp); - } -} - -int vfo_encoder_get_pos() { - int pos=vfoEncoderPos; - - if(strcmp(unameData.nodename,"raspberrypi")==0) { - if(pos<0 && pos>-12) { - pos=0; - } else if(pos>0 && pos<12) { - pos=0; - } - pos=pos/12; - vfoEncoderPos=vfoEncoderPos-(pos*12); - } else if(strcmp(unameData.nodename,"odroid")==0) { - vfoEncoderPos=0; - } - return pos; -} - -int af_encoder_get_pos() { - int pos=afEncoderPos; - afEncoderPos=0; - return pos; -} - -int af_function_get_state() { - return afFunction; -} - -int rf_encoder_get_pos() { - int pos=rfEncoderPos; - rfEncoderPos=0; - return pos; -} - -int rf_function_get_state() { - return rfFunction; -} - -int function_get_state() { - return function; -} - -int band_get_state() { - return band; -} diff --git a/rotary_encoder.h b/rotary_encoder.h deleted file mode 100644 index 604feb1..0000000 --- a/rotary_encoder.h +++ /dev/null @@ -1,6 +0,0 @@ -int encoder_init(); -void encoder_close(); -int vfo_encoder_get_pos(); -int af_encoder_get_pos(); -int af_function_get_state(); - diff --git a/splash.c b/splash.c index 53c7909..a231547 100644 --- a/splash.c +++ b/splash.c @@ -1,6 +1,28 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include +#include "version.h" GtkWidget *splash_window; +GtkWidget *status; +static cairo_surface_t *splash_surface = NULL; /* Close the splash screen */ void splash_close() @@ -8,16 +30,70 @@ void splash_close() gtk_widget_destroy(splash_window); } +static gboolean splash_configure_event_cb (GtkWidget *widget, + GdkEventConfigure *event, + gpointer data) +{ + if (splash_surface) + cairo_surface_destroy (splash_surface); + + splash_surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget), + CAIRO_CONTENT_COLOR, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + + return TRUE; +} + void splash_show(char* image_name,int time,int width,int height) { GtkWidget *image; splash_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_fullscreen(GTK_WINDOW(splash_window)); gtk_widget_set_size_request(splash_window, width, height); gtk_window_set_decorated(GTK_WINDOW(splash_window), FALSE); gtk_window_set_position(GTK_WINDOW(splash_window),GTK_WIN_POS_CENTER_ALWAYS); gtk_window_set_resizable(GTK_WINDOW(splash_window), FALSE); + + + GtkWidget *grid = gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(grid),FALSE); + gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE); + image=gtk_image_new_from_file(image_name); - gtk_container_add(GTK_CONTAINER(splash_window), image); + //gtk_container_add(GTK_CONTAINER(splash_window), image); + gtk_grid_attach(GTK_GRID(grid), image, 0, 0, 1, 4); + g_signal_connect (splash_window,"configure-event", + G_CALLBACK (splash_configure_event_cb), NULL); + + char build[64]; + sprintf(build,"build: %s %s",build_date, build_time); + + GtkWidget *pi_label=gtk_label_new("pihpsdr by John Melton g0orx/n6lyt"); + gtk_label_set_justify(GTK_LABEL(pi_label),GTK_JUSTIFY_LEFT); + gtk_widget_show(pi_label); + gtk_grid_attach(GTK_GRID(grid),pi_label,1,0,1,1); + GtkWidget *build_date_label=gtk_label_new(build); + gtk_label_set_justify(GTK_LABEL(build_date_label),GTK_JUSTIFY_LEFT); + gtk_widget_show(build_date_label); + gtk_grid_attach(GTK_GRID(grid),build_date_label,1,1,1,1); + + status=gtk_label_new(""); + gtk_label_set_justify(GTK_LABEL(status),GTK_JUSTIFY_LEFT); + gtk_widget_override_font(status, pango_font_description_from_string("Arial 18")); + gtk_widget_show(status); + //gtk_container_add(GTK_CONTAINER(splash_window), status); + gtk_grid_attach(GTK_GRID(grid), status, 1, 3, 1, 1); + + gtk_container_add(GTK_CONTAINER(splash_window), grid); gtk_widget_show_all (splash_window); } + +void splash_status(char *text) { + fprintf(stderr,"splash_status: %s\n",text); + gtk_label_set_text(GTK_LABEL(status),text); + usleep(50000); + while (gtk_events_pending ()) + gtk_main_iteration (); +} diff --git a/splash.h b/splash.h index 5adb9ef..b366305 100644 --- a/splash.h +++ b/splash.h @@ -1,5 +1,25 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + extern GtkWidget* splash_window; void splash_close(void); -void splash_show(char* image_name,int time,int width,int height); +void splash_show(char *image_name,int time,int width,int height); +void splash_status(char *text); diff --git a/splash.png b/splash.png deleted file mode 100644 index 4b249eb38b1a91effa9bcbecf04a24d59ac33018..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19869 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 QKz}iKy85}Sb4q9e0Nd@PF8}}l diff --git a/toolbar.c b/toolbar.c index 048ad04..95688c3 100644 --- a/toolbar.c +++ b/toolbar.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #include #include @@ -5,11 +24,11 @@ #include "toolbar.h" #include "mode.h" #include "filter.h" +#include "frequency.h" #include "bandstack.h" #include "band.h" #include "discovered.h" #include "new_protocol.h" -#include "rotary_encoder.h" #include "vfo.h" #include "alex.h" #include "agc.h" @@ -21,25 +40,37 @@ static int width; static int height; -static int column=0; - static GtkWidget *parent_window; +static GtkWidget *sliders; static GtkWidget *toolbar; -static GtkWidget *toolbar_bottom; -static GtkWidget *toolbar_top; static GtkWidget *last_band; static GtkWidget *last_mode; static GtkWidget *last_filter; +#define NONE 0 +#define AF_GAIN 1 +#define MIC_GAIN 2 +#define AGC_GAIN 3 +#define DRIVE 4 +#define TUNE_DRIVE 5 + +#define MIC_GAIN_FUDGE 25.0 + +static gint scale_timer; +static int scale_status=NONE; +static GtkWidget *scale_dialog; static GtkWidget *af_gain_label; -static GtkWidget *audio_scale; +static GtkWidget *af_gain_scale; static GtkWidget *agc_gain_label; static GtkWidget *agc_scale; static GtkWidget *mic_gain_label; -static GtkWidget *mic_scale; +static GtkWidget *mic_gain_scale; +static GtkWidget *drive_label; static GtkWidget *drive_scale; +static GtkWidget *tune_label; static GtkWidget *tune_scale; +static GtkWidget *dummy_label; static GdkRGBA white; static GdkRGBA gray; @@ -57,17 +88,20 @@ static void band_select_cb(GtkWidget *widget, gpointer data) { last_band=widget; gtk_widget_override_background_color(last_band, GTK_STATE_NORMAL, &gray); } - setFrequency(entry->frequencyA); setMode(entry->mode); FILTER* band_filters=filters[entry->mode]; FILTER* band_filter=&band_filters[entry->filter]; setFilter(band_filter->low,band_filter->high); + setFrequency(entry->frequencyA); BAND *band=band_get_current_band(); set_alex_rx_antenna(band->alexRxAntenna); set_alex_tx_antenna(band->alexTxAntenna); set_alex_attenuation(band->alexAttenuation); + vfo_update(NULL); + + setFrequency(entry->frequencyA); } static void band_cb(GtkWidget *widget, gpointer data) { @@ -82,7 +116,7 @@ static void band_cb(GtkWidget *widget, gpointer data) { BAND* band=band_get_band(i); GtkWidget *b=gtk_button_new_with_label(band->title); gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &white); - gtk_widget_override_font(b, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(b, pango_font_description_from_string("Arial 20")); if(i==band_get_current()) { gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &gray); last_band=b; @@ -95,7 +129,7 @@ static void band_cb(GtkWidget *widget, gpointer data) { gtk_container_add(GTK_CONTAINER(content),grid); GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK); - gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20")); gtk_widget_show_all(dialog); g_signal_connect_swapped (dialog, @@ -140,14 +174,14 @@ static void mode_cb(GtkWidget *widget, gpointer data) { } else { gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &white); } - gtk_widget_override_font(b, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(b, pango_font_description_from_string("Arial 20")); gtk_widget_show(b); gtk_grid_attach(GTK_GRID(grid),b,i%5,i/5,1,1); g_signal_connect(b,"pressed",G_CALLBACK(mode_select_cb),(gpointer *)i); } gtk_container_add(GTK_CONTAINER(content),grid); GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK); - gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20")); gtk_widget_show_all(dialog); g_signal_connect_swapped (dialog, @@ -187,7 +221,7 @@ static void filter_cb(GtkWidget *widget, gpointer data) { for(i=0;ifilter) { gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &gray); last_filter=b; @@ -200,7 +234,7 @@ static void filter_cb(GtkWidget *widget, gpointer data) { } gtk_container_add(GTK_CONTAINER(content),grid); GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK); - gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20")); gtk_widget_show_all(dialog); g_signal_connect_swapped (dialog, @@ -212,33 +246,129 @@ static void filter_cb(GtkWidget *widget, gpointer data) { } +int scale_timeout_cb(gpointer data) { +fprintf(stderr,"scale_timeout_cb\n"); + gtk_widget_destroy(scale_dialog); + scale_status=NONE; + return FALSE; +} + static void agc_select_cb(GtkWidget *widget, gpointer data) { agc=(int)data; SetRXAAGCMode(CHANNEL_RX0, agc); } static void agcgain_value_changed_cb(GtkWidget *widget, gpointer data) { - agc_gain=gtk_range_get_value(GTK_RANGE(widget)); + agc_gain=gtk_range_get_value(GTK_RANGE(agc_scale)); SetRXAAGCTop(CHANNEL_RX0, agc_gain); } void set_agc_gain(double value) { - agc_gain=value; + agc_gain=value; + SetRXAAGCTop(CHANNEL_RX0, agc_gain); + if(display_sliders) { gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain); - SetRXAAGCTop(CHANNEL_RX0, agc_gain); + } else { + if(scale_status!=AGC_GAIN) { + if(scale_status!=NONE) { + g_source_remove(scale_timer); + gtk_widget_destroy(scale_dialog); + scale_status=NONE; + } + } + if(scale_status==NONE) { + scale_status=AGC_GAIN; + scale_dialog=gtk_dialog_new_with_buttons("AGC Gain",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(scale_dialog)); + agc_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 120.0, 1.00); + gtk_widget_set_size_request (agc_scale, 400, 30); + gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain); + gtk_widget_show(agc_scale); + gtk_container_add(GTK_CONTAINER(content),agc_scale); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + //gtk_widget_show_all(scale_dialog); + int result=gtk_dialog_run(GTK_DIALOG(scale_dialog)); + } else { + g_source_remove(scale_timer); + gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + } + } } static void afgain_value_changed_cb(GtkWidget *widget, gpointer data) { - volume=gtk_range_get_value(GTK_RANGE(widget)); + volume=gtk_range_get_value(GTK_RANGE(af_gain_scale))/100.0; } void set_af_gain(double value) { - volume=value; - gtk_range_set_value (GTK_RANGE(audio_scale),volume); + volume=value; + if(display_sliders) { + gtk_range_set_value (GTK_RANGE(af_gain_scale),volume*100.0); + } else { + if(scale_status!=AF_GAIN) { + if(scale_status!=NONE) { + g_source_remove(scale_timer); + gtk_widget_destroy(scale_dialog); + scale_status=NONE; + } + } + if(scale_status==NONE) { + scale_status=AF_GAIN; + scale_dialog=gtk_dialog_new_with_buttons("AF Gain",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(scale_dialog)); + af_gain_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.00); + gtk_widget_set_size_request (af_gain_scale, 400, 30); + gtk_range_set_value (GTK_RANGE(af_gain_scale),volume*100.0); + gtk_widget_show(af_gain_scale); + gtk_container_add(GTK_CONTAINER(content),af_gain_scale); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + //gtk_widget_show_all(scale_dialog); + int result=gtk_dialog_run(GTK_DIALOG(scale_dialog)); + } else { + g_source_remove(scale_timer); + gtk_range_set_value (GTK_RANGE(af_gain_scale),volume*100.0); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + } + } } static void micgain_value_changed_cb(GtkWidget *widget, gpointer data) { - mic_gain=gtk_range_get_value(GTK_RANGE(widget)); + mic_gain=gtk_range_get_value(GTK_RANGE(widget))/MIC_GAIN_FUDGE; +fprintf(stderr,"micgain_value_changed: %f\n",mic_gain); +} + +void set_mic_gain(double value) { + mic_gain=value; +fprintf(stderr,"set_mic_gain: %f\n",mic_gain); + if(display_sliders) { + gtk_range_set_value (GTK_RANGE(mic_gain_scale),mic_gain*MIC_GAIN_FUDGE); + } else { + if(scale_status!=MIC_GAIN) { + if(scale_status!=NONE) { + g_source_remove(scale_timer); + gtk_widget_destroy(scale_dialog); + scale_status=NONE; + } + } + if(scale_status==NONE) { + scale_status=MIC_GAIN; + scale_dialog=gtk_dialog_new_with_buttons("Mic Gain",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(scale_dialog)); + mic_gain_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.00); + gtk_widget_set_size_request (mic_gain_scale, 400, 30); + gtk_range_set_value (GTK_RANGE(mic_gain_scale),mic_gain*MIC_GAIN_FUDGE); + gtk_widget_show(mic_gain_scale); + gtk_container_add(GTK_CONTAINER(content),mic_gain_scale); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + //gtk_widget_show_all(scale_dialog); + int result=gtk_dialog_run(GTK_DIALOG(scale_dialog)); + } else { + g_source_remove(scale_timer); + gtk_range_set_value (GTK_RANGE(mic_gain_scale),mic_gain*MIC_GAIN_FUDGE); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + } + + } } static void nr_cb(GtkWidget *widget, gpointer data) { @@ -261,50 +391,6 @@ static void snb_cb(GtkWidget *widget, gpointer data) { SetRXASNBARun(CHANNEL_RX0, snb); } -static void linein_cb(GtkWidget *widget, gpointer data) { - if((orion_mic&0x01)==LINE_IN) { - orion_mic=orion_mic&0xFE; - } else { - orion_mic=orion_mic|LINE_IN; - } -} - -static void micboost_cb(GtkWidget *widget, gpointer data) { - if((orion_mic&0x02)==MIC_BOOST) { - orion_mic=orion_mic&0xFD; - } else { - orion_mic=orion_mic|MIC_BOOST; - } -} - -static void byteswap_cb(GtkWidget *widget, gpointer data) { - byte_swap=byte_swap?0:1; -} - -static void ptt_cb(GtkWidget *widget, gpointer data) { - if((orion_mic&0x04)==ORION_MIC_PTT_ENABLED) { - orion_mic=orion_mic|ORION_MIC_PTT_DISABLED; - } else { - orion_mic=orion_mic&0xFB; - } -} - -static void ptt_ring_cb(GtkWidget *widget, gpointer data) { - if((orion_mic&0x08)==ORION_MIC_PTT_RING_BIAS_TIP) { - orion_mic=orion_mic|ORION_MIC_PTT_TIP_BIAS_RING; - } else { - orion_mic=orion_mic&0xF7; - } -} - -static void bias_cb(GtkWidget *widget, gpointer data) { - if((orion_mic&0x10)==ORION_MIC_BIAS_DISABLED) { - orion_mic=orion_mic|ORION_MIC_BIAS_ENABLED; - } else { - orion_mic=orion_mic&0xEF; - } -} - static void audio_cb(GtkWidget *widget, gpointer data) { GtkWidget *dialog=gtk_dialog_new_with_buttons("Audio",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); @@ -315,35 +401,35 @@ static void audio_cb(GtkWidget *widget, gpointer data) { gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE); GtkWidget *b_off=gtk_radio_button_new_with_label(NULL,"Off"); - gtk_widget_override_font(b_off, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(b_off, pango_font_description_from_string("Arial 16")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_off), agc==AGC_OFF); gtk_widget_show(b_off); gtk_grid_attach(GTK_GRID(grid),b_off,0,0,2,1); g_signal_connect(b_off,"pressed",G_CALLBACK(agc_select_cb),(gpointer *)AGC_OFF); GtkWidget *b_long=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(b_off),"Long"); - gtk_widget_override_font(b_long, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(b_long, pango_font_description_from_string("Arial 16")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_long), agc==AGC_LONG); gtk_widget_show(b_long); gtk_grid_attach(GTK_GRID(grid),b_long,0,1,2,1); g_signal_connect(b_long,"pressed",G_CALLBACK(agc_select_cb),(gpointer *)AGC_LONG); GtkWidget *b_slow=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(b_long),"Slow"); - gtk_widget_override_font(b_slow, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(b_slow, pango_font_description_from_string("Arial 16")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_slow), agc==AGC_SLOW); gtk_widget_show(b_slow); gtk_grid_attach(GTK_GRID(grid),b_slow,0,2,2,1); g_signal_connect(b_slow,"pressed",G_CALLBACK(agc_select_cb),(gpointer *)AGC_SLOW); GtkWidget *b_medium=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(b_slow),"Medium"); - gtk_widget_override_font(b_medium, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(b_medium, pango_font_description_from_string("Arial 16")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_medium), agc==AGC_MEDIUM); gtk_widget_show(b_medium); gtk_grid_attach(GTK_GRID(grid),b_medium,0,3,2,1); g_signal_connect(b_medium,"pressed",G_CALLBACK(agc_select_cb),(gpointer *)AGC_MEDIUM); GtkWidget *b_fast=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(b_medium),"Fast"); - gtk_widget_override_font(b_fast, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(b_fast, pango_font_description_from_string("Arial 16")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_fast), agc==AGC_FAST); gtk_widget_show(b_fast); gtk_grid_attach(GTK_GRID(grid),b_fast,0,4,2,1); @@ -351,28 +437,28 @@ static void audio_cb(GtkWidget *widget, gpointer data) { GtkWidget *b_nr=gtk_check_button_new_with_label("NR"); - gtk_widget_override_font(b_nr, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(b_nr, pango_font_description_from_string("Arial 16")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_nr), nr==1); gtk_widget_show(b_nr); gtk_grid_attach(GTK_GRID(grid),b_nr,2,0,2,1); g_signal_connect(b_nr,"toggled",G_CALLBACK(nr_cb),NULL); GtkWidget *b_nb=gtk_check_button_new_with_label("NB"); - gtk_widget_override_font(b_nb, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(b_nb, pango_font_description_from_string("Arial 16")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_nb), nb==1); gtk_widget_show(b_nb); gtk_grid_attach(GTK_GRID(grid),b_nb,2,1,2,1); g_signal_connect(b_nb,"toggled",G_CALLBACK(nb_cb),NULL); GtkWidget *b_anf=gtk_check_button_new_with_label("ANF"); - gtk_widget_override_font(b_anf, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(b_anf, pango_font_description_from_string("Arial 16")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_anf), anf==1); gtk_widget_show(b_anf); gtk_grid_attach(GTK_GRID(grid),b_anf,2,2,2,1); g_signal_connect(b_anf,"toggled",G_CALLBACK(anf_cb),NULL); - GtkWidget *b_snb=gtk_check_button_new_with_label("SNB"); - gtk_widget_override_font(b_snb, pango_font_description_from_string("Arial 18")); + GtkWidget *b_snb=gtk_check_button_new_with_label("NR2"); + //gtk_widget_override_font(b_snb, pango_font_description_from_string("Arial 16")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_snb), snb==1); gtk_widget_show(b_snb); gtk_grid_attach(GTK_GRID(grid),b_snb,2,3,2,1); @@ -380,7 +466,7 @@ static void audio_cb(GtkWidget *widget, gpointer data) { gtk_container_add(GTK_CONTAINER(content),grid); GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK); - gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 16")); gtk_widget_show_all(dialog); g_signal_connect_swapped (dialog, @@ -394,29 +480,92 @@ static void audio_cb(GtkWidget *widget, gpointer data) { void set_drive(double value) { setDrive(value); - gtk_range_set_value (GTK_RANGE(drive_scale),value); + if(display_sliders) { + gtk_range_set_value (GTK_RANGE(drive_scale),value*100.0); + } else { + if(scale_status!=DRIVE) { + if(scale_status!=NONE) { + g_source_remove(scale_timer); + gtk_widget_destroy(scale_dialog); + scale_status=NONE; + } + } + if(scale_status==NONE) { + scale_status=DRIVE; + scale_dialog=gtk_dialog_new_with_buttons("Drive",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(scale_dialog)); + drive_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.00); + gtk_widget_set_size_request (drive_scale, 400, 30); + gtk_range_set_value (GTK_RANGE(drive_scale),value*100.0); + gtk_widget_show(drive_scale); + gtk_container_add(GTK_CONTAINER(content),drive_scale); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + //gtk_widget_show_all(scale_dialog); + int result=gtk_dialog_run(GTK_DIALOG(scale_dialog)); + } else { + g_source_remove(scale_timer); + gtk_range_set_value (GTK_RANGE(drive_scale),value*100.0); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + } + } } static void drive_value_changed_cb(GtkWidget *widget, gpointer data) { - setDrive(gtk_range_get_value(GTK_RANGE(widget))); + setDrive(gtk_range_get_value(GTK_RANGE(drive_scale))/100.0); } void set_tune(double value) { setTuneDrive(value); - gtk_range_set_value (GTK_RANGE(tune_scale),value); + if(display_sliders) { + gtk_range_set_value (GTK_RANGE(tune_scale),value*100.0); + } else { + if(scale_status!=TUNE_DRIVE) { + if(scale_status!=NONE) { + g_source_remove(scale_timer); + gtk_widget_destroy(scale_dialog); + scale_status=NONE; + } + } + if(scale_status==NONE) { + scale_status=TUNE_DRIVE; + scale_dialog=gtk_dialog_new_with_buttons("Tune Drive",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(scale_dialog)); + tune_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.00); + gtk_widget_set_size_request (tune_scale, 400, 30); + gtk_range_set_value (GTK_RANGE(tune_scale),value*100.0); + gtk_widget_show(tune_scale); + gtk_container_add(GTK_CONTAINER(content),tune_scale); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + //gtk_widget_show_all(scale_dialog); + int result=gtk_dialog_run(GTK_DIALOG(scale_dialog)); + } else { + g_source_remove(scale_timer); + gtk_range_set_value (GTK_RANGE(tune_scale),value*100.0); + scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL); + } + } } static void tune_value_changed_cb(GtkWidget *widget, gpointer data) { - setTuneDrive(gtk_range_get_value(GTK_RANGE(widget))); + setTuneDrive(gtk_range_get_value(GTK_RANGE(tune_scale))/100.0); +} + +static void stop() { + if(protocol==ORIGINAL_PROTOCOL) { + old_protocol_stop(); + } else { + new_protocol_stop(); + } + gpio_close(); } static void yes_cb(GtkWidget *widget, gpointer data) { - encoder_close(); + stop(); _exit(0); } static void halt_cb(GtkWidget *widget, gpointer data) { - encoder_close(); + stop(); system("shutdown -h -P now"); _exit(0); } @@ -424,9 +573,8 @@ static void halt_cb(GtkWidget *widget, gpointer data) { static void exit_cb(GtkWidget *widget, gpointer data) { radioSaveState(); - saveProperties(property_path); - GtkWidget *dialog=gtk_dialog_new_with_buttons("Audio",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + GtkWidget *dialog=gtk_dialog_new_with_buttons("Exit",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog)); GtkWidget *grid=gtk_grid_new(); @@ -435,25 +583,25 @@ static void exit_cb(GtkWidget *widget, gpointer data) { gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE); GtkWidget *label=gtk_label_new("Exit?"); - gtk_widget_override_font(label, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(label, pango_font_description_from_string("Arial 18")); gtk_widget_show(label); gtk_grid_attach(GTK_GRID(grid),label,1,0,1,1); - GtkWidget *b_yes=gtk_button_new_with_label("Yes (to CLI)"); - gtk_widget_override_font(b_yes, pango_font_description_from_string("Arial 18")); + GtkWidget *b_yes=gtk_button_new_with_label("Yes"); + //gtk_widget_override_font(b_yes, pango_font_description_from_string("Arial 18")); gtk_widget_show(b_yes); gtk_grid_attach(GTK_GRID(grid),b_yes,0,1,1,1); g_signal_connect(b_yes,"pressed",G_CALLBACK(yes_cb),NULL); GtkWidget *b_halt=gtk_button_new_with_label("Halt System"); - gtk_widget_override_font(b_halt, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(b_halt, pango_font_description_from_string("Arial 18")); gtk_widget_show(b_halt); gtk_grid_attach(GTK_GRID(grid),b_halt,2,1,1,1); g_signal_connect(b_halt,"pressed",G_CALLBACK(halt_cb),NULL); gtk_container_add(GTK_CONTAINER(content),grid); GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Cancel",GTK_RESPONSE_OK); - gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18")); gtk_widget_show_all(dialog); g_signal_connect_swapped (dialog, @@ -465,52 +613,6 @@ static void exit_cb(GtkWidget *widget, gpointer data) { } -static void apollo_cb(GtkWidget *widget, gpointer data); - -static void alex_cb(GtkWidget *widget, gpointer data) { -fprintf(stderr,"alex_cb\n"); - if(filter_board==ALEX) { -fprintf(stderr,"alex_cb: was ALEX setting NONE\n"); - filter_board=NONE; - } else if(filter_board==NONE) { -fprintf(stderr,"alex_cb: was NONE setting ALEX\n"); - filter_board=ALEX; - } else if(filter_board==APOLLO) { -fprintf(stderr,"alex_cb: was APOLLO setting ALEX\n"); - GtkWidget *w=(GtkWidget *)data; - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE); - filter_board=ALEX; - } - filter_board_changed(); -} - -static void apollo_cb(GtkWidget *widget, gpointer data) { -fprintf(stderr,"apollo_cb\n"); - if(filter_board==APOLLO) { -fprintf(stderr,"apollo_cb: was APOLLO setting NONE\n"); - filter_board=NONE; - } else if(filter_board==NONE) { -fprintf(stderr,"apollo_cb: was NONE setting APOLLO\n"); - filter_board=APOLLO; - } else if(filter_board==ALEX) { -fprintf(stderr,"apollo_cb: was ALEX setting APOLLO\n"); - GtkWidget *w=(GtkWidget *)data; - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE); - filter_board=APOLLO; - } - filter_board_changed(); -} - -static void apollo_tuner_cb(GtkWidget *widget, gpointer data) { - apollo_tuner=apollo_tuner==1?0:1; - tuner_changed(); -} - -static void pa_cb(GtkWidget *widget, gpointer data) { - pa=pa==1?0:1; - pa_changed(); -} - static void cw_keyer_internal_cb(GtkWidget *widget, gpointer data) { cw_keyer_internal=cw_keyer_internal==1?0:1; cw_changed(); @@ -541,6 +643,30 @@ static void cw_keyer_mode_cb(GtkWidget *widget, gpointer data) { cw_changed(); } +static void vfo_divisor_value_changed_cb(GtkWidget *widget, gpointer data) { + vfo_encoder_divisor=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void panadapter_high_value_changed_cb(GtkWidget *widget, gpointer data) { + panadapter_high=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void panadapter_low_value_changed_cb(GtkWidget *widget, gpointer data) { + panadapter_low=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void waterfall_high_value_changed_cb(GtkWidget *widget, gpointer data) { + waterfall_high=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void waterfall_low_value_changed_cb(GtkWidget *widget, gpointer data) { + waterfall_low=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); +} + +static void waterfall_automatic_cb(GtkWidget *widget, gpointer data) { + waterfall_automatic=waterfall_automatic==1?0:1; +} + static void config_cb(GtkWidget *widget, gpointer data) { GtkWidget *dialog=gtk_dialog_new_with_buttons("Audio",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog)); @@ -548,83 +674,92 @@ static void config_cb(GtkWidget *widget, gpointer data) { gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE); gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE); - GtkWidget *linein_b=gtk_check_button_new_with_label("Line In"); - gtk_widget_override_font(linein_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (linein_b), (orion_mic&0x01)==LINE_IN); - gtk_widget_show(linein_b); - gtk_grid_attach(GTK_GRID(grid),linein_b,0,0,1,1); - g_signal_connect(linein_b,"toggled",G_CALLBACK(linein_cb),NULL); - - GtkWidget *micboost_b=gtk_check_button_new_with_label("Boost"); - gtk_widget_override_font(micboost_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (micboost_b), (orion_mic&0x02)==MIC_BOOST); - gtk_widget_show(micboost_b); - gtk_grid_attach(GTK_GRID(grid),micboost_b,0,1,1,1); - g_signal_connect(micboost_b,"toggled",G_CALLBACK(micboost_cb),NULL); - - GtkWidget *byteswap_b=gtk_check_button_new_with_label("Byte swap"); - gtk_widget_override_font(byteswap_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (byteswap_b), byte_swap); - gtk_widget_show(byteswap_b); - gtk_grid_attach(GTK_GRID(grid),byteswap_b,0,2,1,1); - g_signal_connect(byteswap_b,"toggled",G_CALLBACK(byteswap_cb),NULL); - - DISCOVERED* d=&discovered[selected_device]; - if(d->device==NEW_DEVICE_ORION) { - GtkWidget *ptt_b=gtk_check_button_new_with_label("PTT Enabled"); - gtk_widget_override_font(ptt_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_b), (orion_mic&0x04)==ORION_MIC_PTT_ENABLED); - gtk_widget_show(ptt_b); - gtk_grid_attach(GTK_GRID(grid),ptt_b,0,3,1,1); - g_signal_connect(ptt_b,"toggled",G_CALLBACK(ptt_cb),NULL); - - GtkWidget *ptt_ring_b=gtk_check_button_new_with_label("PTT On Ring"); - gtk_widget_override_font(ptt_ring_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_ring_b), (orion_mic&0x08)==ORION_MIC_PTT_RING_BIAS_TIP); - gtk_widget_show(ptt_ring_b); - gtk_grid_attach(GTK_GRID(grid),ptt_ring_b,0,4,1,1); - g_signal_connect(ptt_ring_b,"toggled",G_CALLBACK(ptt_ring_cb),NULL); - - GtkWidget *bias_b=gtk_check_button_new_with_label("BIAS Enabled"); - gtk_widget_override_font(bias_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bias_b), (orion_mic&0x10)==ORION_MIC_BIAS_ENABLED); - gtk_widget_show(bias_b); - gtk_grid_attach(GTK_GRID(grid),bias_b,0,5,1,1); - g_signal_connect(bias_b,"toggled",G_CALLBACK(bias_cb),NULL); - } - - GtkWidget *alex_b=gtk_check_button_new_with_label("ALEX"); - gtk_widget_override_font(alex_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (alex_b), filter_board==ALEX); - gtk_widget_show(alex_b); - gtk_grid_attach(GTK_GRID(grid),alex_b,1,0,1,1); - - GtkWidget *apollo_b=gtk_check_button_new_with_label("APOLLO"); - gtk_widget_override_font(apollo_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apollo_b), filter_board==APOLLO); - gtk_widget_show(apollo_b); - gtk_grid_attach(GTK_GRID(grid),apollo_b,1,1,1,1); - - g_signal_connect(alex_b,"toggled",G_CALLBACK(alex_cb),apollo_b); - g_signal_connect(apollo_b,"toggled",G_CALLBACK(apollo_cb),alex_b); - - GtkWidget *apollo_tuner_b=gtk_check_button_new_with_label("Auto Tuner"); - gtk_widget_override_font(apollo_tuner_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apollo_tuner_b), apollo_tuner); - gtk_widget_show(apollo_tuner_b); - gtk_grid_attach(GTK_GRID(grid),apollo_tuner_b,1,2,1,1); - g_signal_connect(apollo_tuner_b,"toggled",G_CALLBACK(apollo_tuner_cb),NULL); - - GtkWidget *pa_b=gtk_check_button_new_with_label("PA"); - gtk_widget_override_font(pa_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pa_b), pa); - gtk_widget_show(pa_b); - gtk_grid_attach(GTK_GRID(grid),pa_b,1,3,1,1); - g_signal_connect(pa_b,"toggled",G_CALLBACK(pa_cb),NULL); + GtkWidget *display_label=gtk_label_new("Display: "); + //gtk_widget_override_font(display_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(display_label); + gtk_grid_attach(GTK_GRID(grid),display_label,0,0,1,1); + + GtkWidget *panadapter_high_label=gtk_label_new("Panadapter High: "); + //gtk_widget_override_font(panadapter_high_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(panadapter_high_label); + gtk_grid_attach(GTK_GRID(grid),panadapter_high_label,0,1,1,1); + + GtkWidget *panadapter_high_r=gtk_spin_button_new_with_range(-220.0,100.0,1.0); + //gtk_widget_override_font(panadapter_high_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(panadapter_high_r),(double)panadapter_high); + gtk_widget_show(panadapter_high_r); + gtk_grid_attach(GTK_GRID(grid),panadapter_high_r,1,1,1,1); + g_signal_connect(panadapter_high_r,"value_changed",G_CALLBACK(panadapter_high_value_changed_cb),NULL); + + GtkWidget *panadapter_low_label=gtk_label_new("Panadapter Low: "); + //gtk_widget_override_font(panadapter_low_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(panadapter_low_label); + gtk_grid_attach(GTK_GRID(grid),panadapter_low_label,0,2,1,1); + + GtkWidget *panadapter_low_r=gtk_spin_button_new_with_range(-220.0,100.0,1.0); + //gtk_widget_override_font(panadapter_low_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(panadapter_low_r),(double)panadapter_low); + gtk_widget_show(panadapter_low_r); + gtk_grid_attach(GTK_GRID(grid),panadapter_low_r,1,2,1,1); + g_signal_connect(panadapter_low_r,"value_changed",G_CALLBACK(panadapter_low_value_changed_cb),NULL); + + GtkWidget *waterfall_automatic_label=gtk_label_new("Waterfall Automatic: "); + //gtk_widget_override_font(waterfall_automatic_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(waterfall_automatic_label); + gtk_grid_attach(GTK_GRID(grid),waterfall_automatic_label,0,3,1,1); + + GtkWidget *waterfall_automatic_b=gtk_check_button_new(); + ////gtk_widget_override_font(waterfall_automatic_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (waterfall_automatic_b), waterfall_automatic); + gtk_widget_show(waterfall_automatic_b); + gtk_grid_attach(GTK_GRID(grid),waterfall_automatic_b,1,3,1,1); + g_signal_connect(waterfall_automatic_b,"toggled",G_CALLBACK(waterfall_automatic_cb),NULL); + + GtkWidget *waterfall_high_label=gtk_label_new("Waterfall High: "); + //gtk_widget_override_font(waterfall_high_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(waterfall_high_label); + gtk_grid_attach(GTK_GRID(grid),waterfall_high_label,0,4,1,1); + + GtkWidget *waterfall_high_r=gtk_spin_button_new_with_range(-220.0,100.0,1.0); + //gtk_widget_override_font(waterfall_high_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(waterfall_high_r),(double)waterfall_high); + gtk_widget_show(waterfall_high_r); + gtk_grid_attach(GTK_GRID(grid),waterfall_high_r,1,4,1,1); + g_signal_connect(waterfall_high_r,"value_changed",G_CALLBACK(waterfall_high_value_changed_cb),NULL); + + GtkWidget *waterfall_low_label=gtk_label_new("Waterfall Low: "); + //gtk_widget_override_font(waterfall_low_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(waterfall_low_label); + gtk_grid_attach(GTK_GRID(grid),waterfall_low_label,0,5,1,1); + + GtkWidget *waterfall_low_r=gtk_spin_button_new_with_range(-220.0,100.0,1.0); + //gtk_widget_override_font(waterfall_low_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(waterfall_low_r),(double)waterfall_low); + gtk_widget_show(waterfall_low_r); + gtk_grid_attach(GTK_GRID(grid),waterfall_low_r,1,5,1,1); + g_signal_connect(waterfall_low_r,"value_changed",G_CALLBACK(waterfall_low_value_changed_cb),NULL); + + + GtkWidget *vfo_encoder_label=gtk_label_new("VFO Encoder: "); + //gtk_widget_override_font(vfo_encoder_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(vfo_encoder_label); + gtk_grid_attach(GTK_GRID(grid),vfo_encoder_label,0,6,1,1); + + GtkWidget *vfo_divisor_label=gtk_label_new("Divisor: "); + //gtk_widget_override_font(vfo_divisor_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(vfo_divisor_label); + gtk_grid_attach(GTK_GRID(grid),vfo_divisor_label,0,7,1,1); + + GtkWidget *vfo_divisor=gtk_spin_button_new_with_range(1.0,60.0,1.0); + //gtk_widget_override_font(vfo_divisor, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(vfo_divisor),(double)vfo_encoder_divisor); + gtk_widget_show(vfo_divisor); + gtk_grid_attach(GTK_GRID(grid),vfo_divisor,1,7,1,1); + g_signal_connect(vfo_divisor,"value_changed",G_CALLBACK(vfo_divisor_value_changed_cb),NULL); gtk_container_add(GTK_CONTAINER(content),grid); GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK); - gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18")); gtk_widget_show_all(dialog); g_signal_connect_swapped (dialog, @@ -644,67 +779,57 @@ static void cw_cb(GtkWidget *widget, gpointer data) { GtkWidget *cw_keyer_internal_b=gtk_check_button_new_with_label("CW Internal - Speed (WPM)"); - gtk_widget_override_font(cw_keyer_internal_b, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(cw_keyer_internal_b, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_internal_b), cw_keyer_internal); gtk_widget_show(cw_keyer_internal_b); gtk_grid_attach(GTK_GRID(grid),cw_keyer_internal_b,0,0,1,1); g_signal_connect(cw_keyer_internal_b,"toggled",G_CALLBACK(cw_keyer_internal_cb),NULL); GtkWidget *cw_keyer_speed_b=gtk_spin_button_new_with_range(1.0,60.0,1.0); - gtk_widget_override_font(cw_keyer_speed_b, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(cw_keyer_speed_b, pango_font_description_from_string("Arial 18")); gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_speed_b),(double)cw_keyer_speed); gtk_widget_show(cw_keyer_speed_b); gtk_grid_attach(GTK_GRID(grid),cw_keyer_speed_b,1,0,1,1); g_signal_connect(cw_keyer_speed_b,"value_changed",G_CALLBACK(cw_keyer_speed_value_changed_cb),NULL); GtkWidget *cw_breakin_b=gtk_check_button_new_with_label("CW Break In - Delay (ms)"); - gtk_widget_override_font(cw_breakin_b, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(cw_breakin_b, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_breakin_b), cw_breakin); gtk_widget_show(cw_breakin_b); gtk_grid_attach(GTK_GRID(grid),cw_breakin_b,0,1,1,1); g_signal_connect(cw_breakin_b,"toggled",G_CALLBACK(cw_breakin_cb),NULL); GtkWidget *cw_keyer_hang_time_b=gtk_spin_button_new_with_range(0.0,1000.0,1.0); - gtk_widget_override_font(cw_keyer_hang_time_b, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(cw_keyer_hang_time_b, pango_font_description_from_string("Arial 18")); gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_hang_time_b),(double)cw_keyer_hang_time); gtk_widget_show(cw_keyer_hang_time_b); gtk_grid_attach(GTK_GRID(grid),cw_keyer_hang_time_b,1,1,1,1); g_signal_connect(cw_keyer_hang_time_b,"value_changed",G_CALLBACK(cw_keyer_hang_time_value_changed_cb),NULL); GtkWidget *cw_keyer_straight=gtk_radio_button_new_with_label(NULL,"CW KEYER STRAIGHT"); - gtk_widget_override_font(cw_keyer_straight, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(cw_keyer_straight, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_straight), cw_keyer_mode==KEYER_STRAIGHT); gtk_widget_show(cw_keyer_straight); gtk_grid_attach(GTK_GRID(grid),cw_keyer_straight,0,2,1,1); g_signal_connect(cw_keyer_straight,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_STRAIGHT); GtkWidget *cw_keyer_mode_a=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cw_keyer_straight),"CW KEYER MODE A"); - gtk_widget_override_font(cw_keyer_mode_a, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(cw_keyer_mode_a, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_mode_a), cw_keyer_mode==KEYER_MODE_A); gtk_widget_show(cw_keyer_mode_a); gtk_grid_attach(GTK_GRID(grid),cw_keyer_mode_a,0,3,1,1); g_signal_connect(cw_keyer_mode_a,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_MODE_A); GtkWidget *cw_keyer_mode_b=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cw_keyer_mode_a),"CW KEYER MODE B"); - gtk_widget_override_font(cw_keyer_mode_b, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(cw_keyer_mode_b, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_mode_b), cw_keyer_mode==KEYER_MODE_B); gtk_widget_show(cw_keyer_mode_b); gtk_grid_attach(GTK_GRID(grid),cw_keyer_mode_b,0,4,1,1); g_signal_connect(cw_keyer_mode_b,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_MODE_B); -/* -int cw_keyer_speed=12; // 1-60 WPM -int cw_keyer_weight=30; // 0-100 -int cw_keyer_spacing=0; // 0=on 1=off -int cw_keyer_sidetone_volume=127; // 0-127 -int cw_keyer_ptt_delay=20; // 0-255ms -int cw_keyer_hang_time=10; // ms -int cw_keyer_sidetone_frequency=400; // Hz -*/ - gtk_container_add(GTK_CONTAINER(content),grid); GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK); - gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18")); + //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18")); gtk_widget_show_all(dialog); g_signal_connect_swapped (dialog, @@ -715,86 +840,42 @@ int cw_keyer_sidetone_frequency=400; // Hz int result=gtk_dialog_run(GTK_DIALOG(dialog)); } +/* static void adc_cb(GtkWidget *widget, gpointer data) { int adc0=adc[0]; adc[0]=adc[1]; adc[1]=adc0; char label[16]; - gtk_grid_remove_row(GTK_GRID(toolbar_top),0); + gtk_grid_remove_row(GTK_GRID(toolbar_top_1),0); sprintf(label,"RX0=%d",adc[0]); GtkWidget *rx0=gtk_label_new(label); - gtk_widget_override_font(rx0, pango_font_description_from_string("Arial 16")); + //gtk_widget_override_font(rx0, pango_font_description_from_string("Arial 16")); gtk_widget_show(rx0); - gtk_grid_attach(GTK_GRID(toolbar_top),rx0,0,0,1,1); + gtk_grid_attach(GTK_GRID(toolbar_top_1),rx0,0,0,1,1); sprintf(label,"RX1=%d",adc[1]); GtkWidget *rx1=gtk_label_new(label); - gtk_widget_override_font(rx1, pango_font_description_from_string("Arial 16")); + //gtk_widget_override_font(rx1, pango_font_description_from_string("Arial 16")); gtk_widget_show(rx1); - gtk_grid_attach(GTK_GRID(toolbar_top),rx1,1,0,1,1); + gtk_grid_attach(GTK_GRID(toolbar_top_1),rx1,1,0,1,1); } +*/ -static void lock_cb(GtkWidget *widget, gpointer data) { +void lock_cb(GtkWidget *widget, gpointer data) { locked=locked==1?0:1; vfo_update(NULL); } -static void mox_cb(GtkWidget *widget, gpointer data) { - gtk_grid_remove_row (GTK_GRID(toolbar_top),0); +void mox_cb(GtkWidget *widget, gpointer data) { if(getTune()==1) { setTune(0); } - setMox(getMox()==0?1:0); - vfo_update(NULL); - if(getMox()) { - mic_gain_label=gtk_label_new("Mic Gain:"); - gtk_widget_override_font(mic_gain_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(mic_gain_label); - gtk_grid_attach(GTK_GRID(toolbar_top),mic_gain_label,0,0,1,1); - - mic_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01); - gtk_range_set_value (GTK_RANGE(mic_scale),mic_gain); - gtk_widget_show(mic_scale); - gtk_grid_attach(GTK_GRID(toolbar_top),mic_scale,1,0,2,1); - g_signal_connect(G_OBJECT(mic_scale),"value_changed",G_CALLBACK(micgain_value_changed_cb),NULL); - - GtkWidget *drive_label=gtk_label_new("Drive:"); - gtk_widget_override_font(drive_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(drive_label); - gtk_grid_attach(GTK_GRID(toolbar_top),drive_label,3,0,1,1); - - drive_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01); - gtk_range_set_value (GTK_RANGE(drive_scale),getDrive()); - gtk_widget_show(drive_scale); - gtk_grid_attach(GTK_GRID(toolbar_top),drive_scale,4,0,2,1); - g_signal_connect(G_OBJECT(drive_scale),"value_changed",G_CALLBACK(drive_value_changed_cb),NULL); - } else { - af_gain_label=gtk_label_new("AF:"); - gtk_widget_override_font(af_gain_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(af_gain_label); - gtk_grid_attach(GTK_GRID(toolbar_top),af_gain_label,0,0,1,1); - - audio_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01); - gtk_range_set_value (GTK_RANGE(audio_scale),volume); - gtk_widget_show(audio_scale); - gtk_grid_attach(GTK_GRID(toolbar_top),audio_scale,1,0,2,1); - g_signal_connect(G_OBJECT(audio_scale),"value_changed",G_CALLBACK(afgain_value_changed_cb),NULL); - - agc_gain_label=gtk_label_new("AGC:"); - gtk_widget_override_font(agc_gain_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(agc_gain_label); - gtk_grid_attach(GTK_GRID(toolbar_top),agc_gain_label,3,0,1,1); - - agc_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 120.0, 1.0); - gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain); - gtk_widget_show(agc_scale); - gtk_grid_attach(GTK_GRID(toolbar_top),agc_scale,4,0,2,1); - g_signal_connect(G_OBJECT(agc_scale),"value_changed",G_CALLBACK(agcgain_value_changed_cb),NULL); - + if(canTransmit() || tx_out_of_band) { + setMox(getMox()==0?1:0); + vfo_update(NULL); } - //gtk_widget_queue_draw(toolbar_top); } int ptt_update(void *data) { @@ -811,54 +892,101 @@ int ptt_update(void *data) { return 0; } -static void tune_cb(GtkWidget *widget, gpointer data) { - gtk_grid_remove_row (GTK_GRID(toolbar_top),0); +void tune_cb(GtkWidget *widget, gpointer data) { if(getMox()==1) { setMox(0); } - setTune(getTune()==0?1:0); - vfo_update(NULL); - if(getTune()) { - GtkWidget *tune_label=gtk_label_new("Tune Drive:"); - gtk_widget_override_font(tune_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(tune_label); - gtk_grid_attach(GTK_GRID(toolbar_top),tune_label,0,0,1,1); - - tune_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01); - gtk_range_set_value (GTK_RANGE(tune_scale),getTuneDrive()); - gtk_widget_show(tune_scale); - gtk_grid_attach(GTK_GRID(toolbar_top),tune_scale,1,0,2,1); - g_signal_connect(G_OBJECT(tune_scale),"value_changed",G_CALLBACK(tune_value_changed_cb),NULL); - } else { - af_gain_label=gtk_label_new("AF:"); - gtk_widget_override_font(af_gain_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(af_gain_label); - gtk_grid_attach(GTK_GRID(toolbar_top),af_gain_label,0,0,1,1); - - audio_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01); - gtk_range_set_value (GTK_RANGE(audio_scale),volume); - gtk_widget_show(audio_scale); - gtk_grid_attach(GTK_GRID(toolbar_top),audio_scale,1,0,2,1); - g_signal_connect(G_OBJECT(audio_scale),"value_changed",G_CALLBACK(afgain_value_changed_cb),NULL); - - agc_gain_label=gtk_label_new("AGC:"); - gtk_widget_override_font(agc_gain_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(agc_gain_label); - gtk_grid_attach(GTK_GRID(toolbar_top),agc_gain_label,3,0,1,1); - - agc_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 120.0, 1.0); - gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain); - gtk_widget_show(agc_scale); - gtk_grid_attach(GTK_GRID(toolbar_top),agc_scale,4,0,2,1); - g_signal_connect(G_OBJECT(agc_scale),"value_changed",G_CALLBACK(agcgain_value_changed_cb),NULL); + if(canTransmit() || tx_out_of_band) { + setTune(getTune()==0?1:0); + vfo_update(NULL); } } +GtkWidget *sliders_init(int my_width, int my_height, GtkWidget* parent) { + width=my_width; + height=my_height; + parent_window=parent; + + fprintf(stderr,"sliders_init: width=%d height=%d\n", width,height); + + sliders=gtk_grid_new(); + gtk_widget_set_size_request (sliders, width, height); + gtk_grid_set_row_homogeneous(GTK_GRID(sliders), FALSE); + gtk_grid_set_column_homogeneous(GTK_GRID(sliders),TRUE); + + af_gain_label=gtk_label_new("AF:"); + //gtk_widget_override_font(af_gain_label, pango_font_description_from_string("Arial 16")); + gtk_widget_show(af_gain_label); + gtk_grid_attach(GTK_GRID(sliders),af_gain_label,0,0,1,1); + + af_gain_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.00); + gtk_range_set_value (GTK_RANGE(af_gain_scale),volume*100.0); + gtk_widget_show(af_gain_scale); + gtk_grid_attach(GTK_GRID(sliders),af_gain_scale,1,0,2,1); + g_signal_connect(G_OBJECT(af_gain_scale),"value_changed",G_CALLBACK(afgain_value_changed_cb),NULL); + + agc_gain_label=gtk_label_new("AGC:"); + //gtk_widget_override_font(agc_gain_label, pango_font_description_from_string("Arial 16")); + gtk_widget_show(agc_gain_label); + gtk_grid_attach(GTK_GRID(sliders),agc_gain_label,3,0,1,1); + + agc_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 120.0, 1.0); + gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain); + gtk_widget_show(agc_scale); + gtk_grid_attach(GTK_GRID(sliders),agc_scale,4,0,2,1); + g_signal_connect(G_OBJECT(agc_scale),"value_changed",G_CALLBACK(agcgain_value_changed_cb),NULL); + + + mic_gain_label=gtk_label_new("Mic:"); + //gtk_widget_override_font(mic_gain_label, pango_font_description_from_string("Arial 16")); + gtk_widget_show(mic_gain_label); + gtk_grid_attach(GTK_GRID(sliders),mic_gain_label,0,1,1,1); + + mic_gain_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.0); + gtk_range_set_value (GTK_RANGE(mic_gain_scale),mic_gain*MIC_GAIN_FUDGE); + gtk_widget_show(mic_gain_scale); + gtk_grid_attach(GTK_GRID(sliders),mic_gain_scale,1,1,2,1); + g_signal_connect(G_OBJECT(mic_gain_scale),"value_changed",G_CALLBACK(micgain_value_changed_cb),NULL); + + drive_label=gtk_label_new("Drive:"); + //gtk_widget_override_font(drive_label, pango_font_description_from_string("Arial 16")); + gtk_widget_show(drive_label); + gtk_grid_attach(GTK_GRID(sliders),drive_label,3,1,1,1); + + drive_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.0); + gtk_range_set_value (GTK_RANGE(drive_scale),getDrive()*100.0); + gtk_widget_show(drive_scale); + gtk_grid_attach(GTK_GRID(sliders),drive_scale,4,1,2,1); + g_signal_connect(G_OBJECT(drive_scale),"value_changed",G_CALLBACK(drive_value_changed_cb),NULL); + + tune_label=gtk_label_new("Tune:"); + //gtk_widget_override_font(tune_label, pango_font_description_from_string("Arial 16")); + gtk_widget_show(tune_label); + gtk_grid_attach(GTK_GRID(sliders),tune_label,6,1,1,1); + + tune_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.0); + gtk_range_set_value (GTK_RANGE(tune_scale),getTuneDrive()*100.0); + gtk_widget_show(tune_scale); + gtk_grid_attach(GTK_GRID(sliders),tune_scale,7,1,2,1); + g_signal_connect(G_OBJECT(tune_scale),"value_changed",G_CALLBACK(tune_value_changed_cb),NULL); + + dummy_label=gtk_label_new(" "); + //gtk_widget_override_font(dummy_label, pango_font_description_from_string("Arial 16")); + gtk_widget_show(dummy_label); + gtk_grid_attach(GTK_GRID(sliders),dummy_label,9,1,1,1); + + return sliders; +} + GtkWidget *toolbar_init(int my_width, int my_height, GtkWidget* parent) { width=my_width; height=my_height; parent_window=parent; + int button_width=width/8; + + fprintf(stderr,"toolbar_init: width=%d height=%d button_width=%d\n", width,height,button_width); + white.red=1.0; white.green=1.0; white.blue=1.0; @@ -871,137 +999,40 @@ GtkWidget *toolbar_init(int my_width, int my_height, GtkWidget* parent) { toolbar=gtk_grid_new(); gtk_widget_set_size_request (toolbar, width, height); - gtk_grid_set_row_homogeneous(GTK_GRID(toolbar), TRUE); + gtk_grid_set_column_homogeneous(GTK_GRID(toolbar),TRUE); - toolbar_top=gtk_grid_new(); - gtk_grid_set_column_homogeneous(GTK_GRID(toolbar_top),TRUE); - - toolbar_bottom=gtk_grid_new(); - gtk_grid_set_column_homogeneous(GTK_GRID(toolbar_bottom),FALSE); - GtkWidget *band=gtk_button_new_with_label("Band"); - gtk_widget_override_font(band, pango_font_description_from_string("Arial 20")); + gtk_widget_set_size_request (band, button_width, 0); + //gtk_widget_override_font(band, pango_font_description_from_string("Arial 16")); g_signal_connect(G_OBJECT(band),"clicked",G_CALLBACK(band_cb),NULL); - gtk_widget_show(band); - gtk_grid_attach(GTK_GRID(toolbar_bottom),band,column,0,1,1); - column++; + gtk_grid_attach(GTK_GRID(toolbar),band,0,0,4,1); GtkWidget *mode=gtk_button_new_with_label("Mode"); - gtk_widget_override_font(mode, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(mode, pango_font_description_from_string("Arial 16")); g_signal_connect(G_OBJECT(mode),"clicked",G_CALLBACK(mode_cb),NULL); - gtk_widget_show(mode); - gtk_grid_attach(GTK_GRID(toolbar_bottom),mode,column,0,1,1); - column++; + gtk_grid_attach(GTK_GRID(toolbar),mode,4,0,4,1); GtkWidget *filter=gtk_button_new_with_label("Filter"); - gtk_widget_override_font(filter, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(filter, pango_font_description_from_string("Arial 16")); g_signal_connect(G_OBJECT(filter),"clicked",G_CALLBACK(filter_cb),NULL); - gtk_widget_show(filter); - gtk_grid_attach(GTK_GRID(toolbar_bottom),filter,column,0,1,1); - column++; - - GtkWidget *audio=gtk_button_new_with_label("Audio"); - gtk_widget_override_font(audio, pango_font_description_from_string("Arial 20")); - g_signal_connect(G_OBJECT(audio),"clicked",G_CALLBACK(audio_cb),NULL); - gtk_widget_show(audio); - gtk_grid_attach(GTK_GRID(toolbar_bottom),audio,column,0,1,1); - column++; - - GtkWidget *config=gtk_button_new_with_label("Config"); - gtk_widget_override_font(config, pango_font_description_from_string("Arial 20")); - g_signal_connect(G_OBJECT(config),"clicked",G_CALLBACK(config_cb),NULL); - gtk_widget_show(config); - gtk_grid_attach(GTK_GRID(toolbar_bottom),config,column,0,1,1); - column++; - - GtkWidget *cw=gtk_button_new_with_label("CW"); - gtk_widget_override_font(cw, pango_font_description_from_string("Arial 20")); - g_signal_connect(G_OBJECT(cw),"clicked",G_CALLBACK(cw_cb),NULL); - gtk_widget_show(cw); - gtk_grid_attach(GTK_GRID(toolbar_bottom),cw,column,0,1,1); - column++; - - GtkWidget *exit=gtk_button_new_with_label("Exit"); - gtk_widget_override_font(exit, pango_font_description_from_string("Arial 20")); - g_signal_connect(G_OBJECT(exit),"clicked",G_CALLBACK(exit_cb),NULL); - gtk_widget_show(exit); - gtk_grid_attach(GTK_GRID(toolbar_bottom),exit,column,0,1,1); - column++; + gtk_grid_attach(GTK_GRID(toolbar),filter,8,0,4,1); GtkWidget *lock=gtk_button_new_with_label("Lock"); - gtk_widget_override_font(lock, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(lock, pango_font_description_from_string("Arial 16")); g_signal_connect(G_OBJECT(lock),"clicked",G_CALLBACK(lock_cb),NULL); - gtk_widget_show(lock); - gtk_grid_attach(GTK_GRID(toolbar_bottom),lock,column,0,1,1); - column++; + gtk_grid_attach(GTK_GRID(toolbar),lock,16,0,4,1); GtkWidget *tune=gtk_button_new_with_label("Tune"); - gtk_widget_override_font(tune, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(tune, pango_font_description_from_string("Arial 16")); g_signal_connect(G_OBJECT(tune),"clicked",G_CALLBACK(tune_cb),NULL); - gtk_widget_show(tune); - gtk_grid_attach(GTK_GRID(toolbar_bottom),tune,column,0,1,1); - column++; + gtk_grid_attach(GTK_GRID(toolbar),tune,24,0,4,1); GtkWidget *tx=gtk_button_new_with_label("Mox"); - gtk_widget_override_font(tx, pango_font_description_from_string("Arial 20")); + //gtk_widget_override_font(tx, pango_font_description_from_string("Arial 16")); g_signal_connect(G_OBJECT(tx),"clicked",G_CALLBACK(mox_cb),NULL); - gtk_widget_show(tx); - gtk_grid_attach(GTK_GRID(toolbar_bottom),tx,column,0,1,1); - column++; - - - // default to receive controls on top bar - af_gain_label=gtk_label_new("AF:"); - gtk_widget_override_font(af_gain_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(af_gain_label); - gtk_grid_attach(GTK_GRID(toolbar_top),af_gain_label,0,0,1,1); - - audio_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01); - gtk_range_set_value (GTK_RANGE(audio_scale),volume); - gtk_widget_show(audio_scale); - gtk_grid_attach(GTK_GRID(toolbar_top),audio_scale,1,0,2,1); - g_signal_connect(G_OBJECT(audio_scale),"value_changed",G_CALLBACK(afgain_value_changed_cb),NULL); - - agc_gain_label=gtk_label_new("AGC:"); - gtk_widget_override_font(agc_gain_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(agc_gain_label); - gtk_grid_attach(GTK_GRID(toolbar_top),agc_gain_label,3,0,1,1); - - agc_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 120.0, 1.0); - gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain); - gtk_widget_show(agc_scale); - gtk_grid_attach(GTK_GRID(toolbar_top),agc_scale,4,0,2,1); - g_signal_connect(G_OBJECT(agc_scale),"value_changed",G_CALLBACK(agcgain_value_changed_cb),NULL); - -/* - - GtkWidget *drive_label=gtk_label_new("Drive:"); - gtk_widget_override_font(drive_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(drive_label); - gtk_grid_attach(toolbar_top,drive_label,6,0,1,1); - - drive_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01); - gtk_range_set_value (drive_scale,getTuneDrive()); - gtk_widget_show(drive_scale); - gtk_grid_attach(toolbar_top,drive_scale,7,0,2,1); - g_signal_connect(G_OBJECT(drive_scale),"value_changed",G_CALLBACK(drive_value_changed_cb),NULL); - - GtkWidget *tune_label=gtk_label_new("Tune:"); - gtk_widget_override_font(tune_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(tune_label); - gtk_grid_attach(toolbar_top,tune_label,9,0,1,1); - - tune_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01); - gtk_range_set_value (tune_scale,getTuneDrive()); - gtk_widget_show(tune_scale); - gtk_grid_attach(toolbar_top,tune_scale,10,0,2,1); - g_signal_connect(G_OBJECT(tune_scale),"value_changed",G_CALLBACK(tune_value_changed_cb),NULL); -*/ + gtk_grid_attach(GTK_GRID(toolbar),tx,28,0,4,1); - gtk_grid_attach(GTK_GRID(toolbar),toolbar_top,0,0,1,1); - gtk_widget_show(toolbar_top); - gtk_grid_attach(GTK_GRID(toolbar),toolbar_bottom,0,1,1,1); - gtk_widget_show(toolbar_bottom); + gtk_widget_show_all(toolbar); return toolbar; } diff --git a/toolbar.h b/toolbar.h index 53c25f3..99940ea 100644 --- a/toolbar.h +++ b/toolbar.h @@ -1,6 +1,30 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + void set_agc_gain(double value); void set_af_gain(double value); +void set_mic_gain(double value); void set_drive(double drive); void set_tune(double tune); int ptt_update(void *data); +void lock_cb(GtkWidget *widget, gpointer data); +void mox_cb(GtkWidget *widget, gpointer data); +void tune_cb(GtkWidget *widget, gpointer data); GtkWidget *toolbar_init(int my_width, int my_height, GtkWidget* parent); +GtkWidget *sliders_init(int my_width, int my_height, GtkWidget* parent); diff --git a/version.c b/version.c new file mode 100644 index 0000000..c3bd1b1 --- /dev/null +++ b/version.c @@ -0,0 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +char build_date[]=__DATE__; +char build_time[]=__TIME__; + diff --git a/version.h b/version.h new file mode 100644 index 0000000..a250627 --- /dev/null +++ b/version.h @@ -0,0 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +extern char build_date[]; +extern char build_time[]; + diff --git a/vfo.c b/vfo.c index 648f31d..b0ba261 100644 --- a/vfo.c +++ b/vfo.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #include #include @@ -7,32 +26,29 @@ #include #include +#include "main.h" +#include "agc.h" #include "mode.h" #include "filter.h" #include "bandstack.h" #include "band.h" +#include "frequency.h" #include "new_protocol.h" -#include "rotary_encoder.h" #include "radio.h" #include "vfo.h" #include "channel.h" +#include "gpio.h" #include "wdsp.h" static GtkWidget *parent_window; +static int my_width; +static int my_height; static GtkWidget *vfo; static cairo_surface_t *vfo_surface = NULL; -static pthread_t rotary_encoder_thread_id; - static int steps[]={1,10,25,50,100,250,500,1000,2500,5000,6250,9000,10000,12500,15000,20000,25000,30000,50000,100000,0}; static char *step_labels[]={"1Hz","10Hz","25Hz","50Hz","100Hz","250Hz","500Hz","1kHz","2.5kHz","5kHz","6.25kHz","9kHz","10kHz","12.5kHz","15kHz","20kHz","25kHz","30kHz","50kHz","100kHz",0}; -static int af_function=0; -static int previous_af_function=0; -static int rf_function=0; -static int previous_rf_function=0; -static int band_button=0; -static int previous_band_button=0; static GtkWidget* menu=NULL; static GtkWidget* band_menu=NULL; @@ -55,167 +71,13 @@ void vfo_move(int hz) { } } -static int rotary_encoder_changed(void *data) { - if(!locked) { - int pos=*(int*)data; - BANDSTACK_ENTRY* entry=bandstack_entry_get_current(); - entry->frequencyA=entry->frequencyA+(pos*step); - setFrequency(entry->frequencyA); - vfo_update(NULL); - } - free(data); - return 0; -} - -static int af_encoder_changed(void *data) { - int pos=*(int*)data; - if(pos!=0) { - if(af_function) { - // agc gain - double gain=agc_gain; - gain+=(double)pos; - if(gain<0.0) { - gain=0.0; - } else if(gain>120.0) { - gain=120.0; - } - set_agc_gain(gain); - } else { - // af gain - double gain=volume; - gain+=(double)pos/20.0; - if(gain<0.0) { - gain=0.0; - } else if(gain>1.0) { - gain=1.0; - } - set_af_gain(gain); - } - } - free(data); - return 0; -} - -static int rf_encoder_changed(void *data) { - int pos=*(int*)data; - if(pos!=0) { - if(rf_function) { - // tune drive - double d=getTuneDrive(); - d+=(double)pos/10.0; - if(d<0.0) { - d=0.0; - } else if(d>1.0) { - d=1.0; - } - set_tune(d); - } else { - // drive - double d=getDrive(); - d+=(double)pos/10.0; - if(d<0.0) { - d=0.0; - } else if(d>1.0) { - d=1.0; - } - set_drive(d); - } - } - free(data); - return 0; -} - -static int band_pressed(void *data) { - int function=*(int*)data; - - BANDSTACK_ENTRY *entry; - if(function) { - entry=bandstack_entry_next(); - } else { - int b=band_get_current(); - b++; - if(b>=BANDS) { - b=0; - } - BAND* band=band_set_current(b); - entry=bandstack_entry_get_current(); - } - - setFrequency(entry->frequencyA); - setMode(entry->mode); - FILTER* band_filters=filters[entry->mode]; - FILTER* band_filter=&band_filters[entry->filter]; - setFilter(band_filter->low,band_filter->high); - - BAND *band=band_get_current_band(); - set_alex_rx_antenna(band->alexRxAntenna); - set_alex_tx_antenna(band->alexTxAntenna); - set_alex_attenuation(band->alexAttenuation); - vfo_update(NULL); - - - free(data); - return 0; -} - -static void* rotary_encoder_thread(void *arg) { - int pos; - while(1) { - - pos=vfo_encoder_get_pos(); - if(pos!=0) { - int *p=malloc(sizeof(int)); - *p=pos; - g_idle_add(rotary_encoder_changed,(gpointer)p); - } - - af_function=af_function_get_state(); - if(af_function!=previous_af_function) { - fprintf(stderr,"af_function: %d\n",af_function); - previous_af_function=af_function; - } - pos=af_encoder_get_pos(); - if(pos!=0) { - int *p=malloc(sizeof(int)); - *p=pos; - g_idle_add(af_encoder_changed,(gpointer)p); - } - - rf_function=rf_function_get_state(); - if(rf_function!=previous_rf_function) { - fprintf(stderr,"rf_function: %d\n",rf_function); - previous_rf_function=rf_function; - } - pos=rf_encoder_get_pos(); - if(pos!=0) { - int *p=malloc(sizeof(int)); - *p=pos; - g_idle_add(rf_encoder_changed,(gpointer)p); - } - - int band_button=band_get_state(); - if(band_button!=previous_band_button) { - fprintf(stderr,"band_button: %d\n",band_button); - previous_band_button=band_button; - if(band_button) { - int function=function_get_state(); - g_idle_add(band_pressed,(gpointer)function); - } - } - -#ifdef raspberrypi - gpioDelay(100000); // 10 per second -#endif -#ifdef odroid - usleep(100000); -#endif - } -} - static gboolean vfo_configure_event_cb (GtkWidget *widget, GdkEventConfigure *event, gpointer data) { +fprintf(stderr,"vfo_configure_event_cb: width=%d height=%d\n", + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); if (vfo_surface) cairo_surface_destroy (vfo_surface); @@ -257,7 +119,8 @@ int vfo_update(void *data) { cairo_select_font_face(cr, "Arial", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); - cairo_set_font_size(cr, 36); + //cairo_set_font_size(cr, 36); + cairo_set_font_size(cr, 28); if(isTransmitting()) { cairo_set_source_rgb(cr, 1, 0, 0); @@ -266,14 +129,18 @@ int vfo_update(void *data) { } char sf[32]; - sprintf(sf,"%0lld.%06lld MHz %s %s",entry->frequencyA/(long long)1000000,entry->frequencyA%(long long)1000000,mode_string[entry->mode],band_filter->title); - cairo_move_to(cr, 130, 45); + sprintf(sf,"%0lld.%06lld MHz",entry->frequencyA/(long long)1000000,entry->frequencyA%(long long)1000000); + cairo_move_to(cr, 5, 30); cairo_show_text(cr, sf); - cairo_set_font_size(cr, 18); - sprintf(sf,"Step %dHz",step); - cairo_move_to(cr, 10, 25); - cairo_show_text(cr, sf); + cairo_set_font_size(cr, 12); + + cairo_move_to(cr, (my_width/2)+20, 30); + cairo_show_text(cr, getFrequencyInfo(entry->frequencyA)); + +// sprintf(sf,"Step %dHz",step); +// cairo_move_to(cr, 10, 25); +// cairo_show_text(cr, sf); if(locked) { cairo_set_source_rgb(cr, 1, 0, 0); @@ -281,6 +148,52 @@ int vfo_update(void *data) { cairo_show_text(cr, "Locked"); } + if(function) { + cairo_set_source_rgb(cr, 1, 0.5, 0); + cairo_move_to(cr, 70, 50); + cairo_show_text(cr, "Function"); + } + + cairo_set_source_rgb(cr, 1, 1, 0); + cairo_move_to(cr, 130, 50); + cairo_show_text(cr, mode_string[entry->mode]); + + cairo_move_to(cr, 190, 50); + cairo_show_text(cr, band_filter->title); + + cairo_move_to(cr, 250, 50); + if(nr) { + cairo_show_text(cr, "NR"); + } + if(nb) { + cairo_show_text(cr, "NB"); + } + if(anf) { + cairo_show_text(cr, "ANF"); + } + if(snb) { + cairo_show_text(cr, "NR2"); + } + + cairo_move_to(cr, 310, 50); + switch(agc) { + case AGC_OFF: + cairo_show_text(cr, "AGC OFF"); + break; + case AGC_LONG: + cairo_show_text(cr, "AGC LONG"); + break; + case AGC_SLOW: + cairo_show_text(cr, "AGC SLOW"); + break; + case AGC_MEDIUM: + cairo_show_text(cr, "AGC MEDIUM"); + break; + case AGC_FAST: + cairo_show_text(cr, "AGC FAST"); + break; + } + cairo_destroy (cr); gtk_widget_queue_draw (vfo); } @@ -300,7 +213,7 @@ vfo_press_event_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) { - GtkWidget *dialog=gtk_dialog_new_with_buttons("VFO",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); + GtkWidget *dialog=gtk_dialog_new_with_buttons("Step",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog)); GtkWidget *grid=gtk_grid_new(); @@ -319,12 +232,13 @@ vfo_press_event_cb (GtkWidget *widget, gtk_widget_override_font(step_rb, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (step_rb), steps[i]==step); gtk_widget_show(step_rb); - gtk_grid_attach(GTK_GRID(grid),step_rb,i/5,i%5,1,1); + gtk_grid_attach(GTK_GRID(grid),step_rb,i%5,i/5,1,1); g_signal_connect(step_rb,"pressed",G_CALLBACK(vfo_step_select_cb),(gpointer *)i); i++; } gtk_container_add(GTK_CONTAINER(content),grid); + GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK); gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18")); gtk_widget_show_all(dialog); @@ -341,10 +255,12 @@ vfo_press_event_cb (GtkWidget *widget, GtkWidget* vfo_init(int width,int height,GtkWidget *parent) { +fprintf(stderr,"vfo_init: width=%d height=%d\n", width, height); parent_window=parent; + my_width=width; + my_height=height; vfo = gtk_drawing_area_new (); - /* set a minimum size */ gtk_widget_set_size_request (vfo, width, height); /* Signals used to handle the backing surface */ @@ -353,15 +269,6 @@ GtkWidget* vfo_init(int width,int height,GtkWidget *parent) { g_signal_connect (vfo,"configure-event", G_CALLBACK (vfo_configure_event_cb), NULL); - if(encoder_init() == 0) { - int rc=pthread_create(&rotary_encoder_thread_id, NULL, rotary_encoder_thread, NULL); - if(rc<0) { - fprintf(stderr,"pthread_create for rotary_encoder_thread failed %d\n",rc); - } - } else { - fprintf(stderr,"encoder_init failed\n"); - } - /* Event signals */ g_signal_connect (vfo, "button-press-event", G_CALLBACK (vfo_press_event_cb), NULL); diff --git a/vfo.h b/vfo.h index 22d2da1..008e8e0 100644 --- a/vfo.h +++ b/vfo.h @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + GtkWidget* vfo_init(int width,int height,GtkWidget *parent); void vfo_step(int steps); diff --git a/waterfall.c b/waterfall.c index 176e868..e3dd218 100644 --- a/waterfall.c +++ b/waterfall.c @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + #include #include @@ -5,16 +24,13 @@ #include #include #include +#include "radio.h" #include "vfo.h" -#include "new_protocol.h" #include "waterfall.h" static GtkWidget *waterfall; static GdkPixbuf *pixbuf = NULL; -static float highThreshold = -100.0f; -static float lowThreshold = -150.0f; - static int colorLowR=0; // black static int colorLowG=0; static int colorLowB=0; @@ -144,17 +160,17 @@ void waterfall_update(float *data) { for(i=0;ihighThreshold) { + } else if(sample>(float)waterfall_high) { *p++=colorHighR; *p++=colorHighG; *p++=colorHighB; } else { - float range=highThreshold-lowThreshold; - float offset=sample-lowThreshold; + float range=(float)waterfall_high-(float)waterfall_low; + float offset=sample-(float)waterfall_low; float percent=offset/range; if(percent<(2.0f/9.0f)) { float local_percent = percent / (2.0f/9.0f); @@ -197,8 +213,10 @@ void waterfall_update(float *data) { } - lowThreshold=(float)((average/display_width)); - highThreshold=lowThreshold+50.0; + if(waterfall_automatic) { + waterfall_low=average/display_width; + waterfall_high=waterfall_low+50; + } gtk_widget_queue_draw (waterfall); diff --git a/waterfall.h b/waterfall.h index 7b5e379..c656298 100644 --- a/waterfall.h +++ b/waterfall.h @@ -1,3 +1,22 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + void waterfall_update(float *data); GtkWidget* waterfall_init(int width,int height); diff --git a/wdsp_init.c b/wdsp_init.c new file mode 100644 index 0000000..da15bda --- /dev/null +++ b/wdsp_init.c @@ -0,0 +1,280 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "alex.h" +#include "new_protocol.h" +#include "channel.h" +#include "discovered.h" +#include "mode.h" +#include "filter.h" +#include "wdsp.h" +#include "radio.h" +#include "vfo.h" +#include "toolbar.h" +#include "wdsp_init.h" + +#define PI 3.1415926535897932F + +static int receiver; +static int running=0; + +static int buffer_size=BUFFER_SIZE; +static int tx_buffer_size=BUFFER_SIZE; +static int fft_size=4096; +static int dspRate=48000; +static int outputRate=48000; + +static int micSampleRate=48000; +static int micDspRate=48000; +static int micOutputRate=192000; + +static int spectrumWIDTH=800; +static int SPECTRUM_UPDATES_PER_SECOND=10; + +static void initAnalyzer(int channel,int buffer_size); + +void setMode(int m) { + mode=m; + SetRXAMode(receiver, mode); + SetTXAMode(CHANNEL_TX, mode); +} + +int getMode() { + return mode; +} + +void setFilter(int low,int high) { + if(mode==modeCWL) { + filterLow=-cwPitch-low; + filterHigh=-cwPitch+high; + } else if(mode==modeCWU) { + filterLow=cwPitch-low; + filterHigh=cwPitch+high; + } else { + filterLow=low; + filterHigh=high; + } + + RXANBPSetFreqs(receiver,(double)filterLow,(double)filterHigh); + SetRXABandpassFreqs(receiver, (double)filterLow, (double)filterHigh); + SetRXASNBAOutputBandwidth(receiver, (double)filterLow, (double)filterHigh); + + SetTXABandpassFreqs(CHANNEL_TX, (double)filterLow, (double)filterHigh); +} + +int getFilterLow() { + return filterLow; +} + +int getFilterHigh() { + return filterHigh; +} + +void wdsp_init(int rx,int pixels,int protocol) { + int rc; + receiver=rx; + spectrumWIDTH=pixels; + + fprintf(stderr,"wdsp_init: %d\n",rx); + + if(protocol==ORIGINAL_PROTOCOL) { + micOutputRate=48000; + } else { + micOutputRate=192000; + } + + while (gtk_events_pending ()) + gtk_main_iteration (); + + fprintf(stderr,"OpenChannel %d buffer_size=%d fft_size=%d sample_rate=%d dspRate=%d outputRate=%d\n", + rx, + buffer_size, + fft_size, + sample_rate, + dspRate, + outputRate); + + OpenChannel(rx, + buffer_size, + fft_size, + sample_rate, + dspRate, + outputRate, + 0, // receive + 1, // run + 0.010, 0.025, 0.0, 0.010, 0); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + switch(sample_rate) { + case 48000: + tx_buffer_size=BUFFER_SIZE; + break; + case 96000: + tx_buffer_size=BUFFER_SIZE/2; + break; + case 192000: + tx_buffer_size=BUFFER_SIZE/4; + break; + case 384000: + tx_buffer_size=BUFFER_SIZE/8; + break; + } + fprintf(stderr,"OpenChannel %d buffer_size=%d fft_size=%d sample_rate=%d dspRate=%d outputRate=%d\n", + CHANNEL_TX, + tx_buffer_size, + fft_size, + sample_rate, //micSampleRate, + micDspRate, + micOutputRate); + + OpenChannel(CHANNEL_TX, + buffer_size, + fft_size, + sample_rate, //micSampleRate, + micDspRate, + micOutputRate, + 1, // transmit + 1, // run + 0.010, 0.025, 0.0, 0.010, 0); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + fprintf(stderr,"XCreateAnalyzer %d\n",rx); + int success; + XCreateAnalyzer(rx, &success, 262144, 1, 1, ""); + if (success != 0) { + fprintf(stderr, "XCreateAnalyzer %d failed: %d\n" ,rx,success); + } + initAnalyzer(rx,buffer_size); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + XCreateAnalyzer(CHANNEL_TX, &success, 262144, 1, 1, ""); + if (success != 0) { + fprintf(stderr, "XCreateAnalyzer CHANNEL_TX failed: %d\n" ,success); + } + initAnalyzer(CHANNEL_TX,tx_buffer_size); + + SetRXAMode(rx, mode); + SetRXABandpassFreqs(rx, (double)filterLow, (double)filterHigh); + SetRXAAGCMode(rx, agc); + SetRXAAGCTop(rx,agc_gain); + + SetRXAAMDSBMode(CHANNEL_RX0, 0); + SetRXAShiftRun(CHANNEL_RX0, 0); + SetRXAEMNRgainMethod(CHANNEL_RX0, 1); + SetRXAEMNRnpeMethod(CHANNEL_RX0, 0); + SetRXAEMNRaeRun(CHANNEL_RX0, 1); + SetRXAEMNRPosition(CHANNEL_RX0, 0); + SetRXAEMNRRun(CHANNEL_RX0, 0); + SetRXAANRRun(CHANNEL_RX0, 0); + SetRXAANFRun(CHANNEL_RX0, 0); + + SetTXAMode(CHANNEL_TX, mode); + SetTXABandpassFreqs(CHANNEL_TX, (double)filterLow, (double)filterHigh); + SetTXABandpassWindow(CHANNEL_TX, 1); + SetTXABandpassRun(CHANNEL_TX, 1); + + SetTXACFIRRun(CHANNEL_TX, 1); + SetTXAEQRun(CHANNEL_TX, 0); + SetTXACTCSSRun(CHANNEL_TX, 0); + SetTXAAMSQRun(CHANNEL_TX, 0); + SetTXACompressorRun(CHANNEL_TX, 0); + SetTXAosctrlRun(CHANNEL_TX, 0); + SetTXAPreGenRun(CHANNEL_TX, 0); + SetTXAPostGenRun(CHANNEL_TX, 0); + +} + +static void initAnalyzer(int channel,int buffer_size) { + int flp[] = {0}; + double KEEP_TIME = 0.1; + int n_pixout=1; + int spur_elimination_ffts = 1; + int data_type = 1; + int fft_size = 8192; + int window_type = 4; + double kaiser_pi = 14.0; + int overlap = 2048; + int clip = 0; + int span_clip_l = 0; + int span_clip_h = 0; + int pixels=spectrumWIDTH; + int stitches = 1; + int avm = 0; + double tau = 0.001 * 120.0; + int MAX_AV_FRAMES = 60; + int display_average = MAX(2, (int) MIN((double) MAX_AV_FRAMES, (double) SPECTRUM_UPDATES_PER_SECOND * tau)); + double avb = exp(-1.0 / (SPECTRUM_UPDATES_PER_SECOND * tau)); + int calibration_data_set = 0; + double span_min_freq = 0.0; + double span_max_freq = 0.0; + + int max_w = fft_size + (int) MIN(KEEP_TIME * (double) SPECTRUM_UPDATES_PER_SECOND, KEEP_TIME * (double) fft_size * (double) SPECTRUM_UPDATES_PER_SECOND); + + fprintf(stderr,"SetAnalyzer channel=%d\n",channel); + SetAnalyzer(channel, + n_pixout, + spur_elimination_ffts, //number of LO frequencies = number of ffts used in elimination + data_type, //0 for real input data (I only); 1 for complex input data (I & Q) + flp, //vector with one elt for each LO frequency, 1 if high-side LO, 0 otherwise + fft_size, //size of the fft, i.e., number of input samples + buffer_size, //number of samples transferred for each OpenBuffer()/CloseBuffer() + window_type, //integer specifying which window function to use + kaiser_pi, //PiAlpha parameter for Kaiser window + overlap, //number of samples each fft (other than the first) is to re-use from the previous + clip, //number of fft output bins to be clipped from EACH side of each sub-span + span_clip_l, //number of bins to clip from low end of entire span + span_clip_h, //number of bins to clip from high end of entire span + pixels, //number of pixel values to return. may be either <= or > number of bins + stitches, //number of sub-spans to concatenate to form a complete span +/* + avm, //averaging mode + display_average, //number of spans to (moving) average for pixel result + avb, //back multiplier for weighted averaging +*/ + calibration_data_set, //identifier of which set of calibration data to use + span_min_freq, //frequency at first pixel value8192 + span_max_freq, //frequency at last pixel value + max_w //max samples to hold in input ring buffers + ); +} diff --git a/wdsp_init.h b/wdsp_init.h new file mode 100644 index 0000000..041737d --- /dev/null +++ b/wdsp_init.h @@ -0,0 +1,25 @@ +/* Copyright (C) +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +extern void setMode(int m); +extern int getMode(); +extern void setFilter(int low,int high); +extern int getFilterLow(); +extern int getFilterHigh(); +extern void wdsp_init(int rx,int pixels,int protocol); diff --git a/xvtr.h b/xvtr.h index bc948d6..4c8f448 100644 --- a/xvtr.h +++ b/xvtr.h @@ -1,14 +1,7 @@ -/** -* @file xvtr.h -* @brief XVTR definition files -* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S -* @version 0.1 -* @date 2009-04-11 -*/ -// xvtr.h - /* Copyright (C) -* This program is free software; you can redistribute it and/or2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S +* 2015 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. -- 2.45.2