From 34eaf8a427efb53e0edcd01fbb976ea01d7b6108 Mon Sep 17 00:00:00 2001 From: John Melton g0orx/n6lyt <john.d.melton@googlemail.com> 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 <stdio.h> #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;stack<bands[b].bandstack->entries;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;stack<bands[b].bandstack->entries;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;i<NUM_BAND_LIMITS;i++) { + limits=&bandLimits[i]; + if((minDisplay<=limits->minFrequency&&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 <netinet/in.h> #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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdint.h> +#include <fcntl.h> +#include <poll.h> +#include <sched.h> +#include <pthread.h> +#include <wiringPi.h> +#ifdef raspberrypi +#include <pigpio.h> +#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 && pos<vfo_encoder_divisor) { + pos=0; + } + pos=pos/vfo_encoder_divisor; + vfoEncoderPos=vfoEncoderPos-(pos*vfo_encoder_divisor); + } else { + 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 agc_encoder_get_pos() { + int pos=agcEncoderPos; + agcEncoderPos=0; + return pos; +} + +int rf_function_get_state() { + return rfFunction; +} + +int function_get_state() { + return function_state; +} + +int band_get_state() { + return band_state; +} + +int bandstack_get_state() { + return bandstack_state; +} + +int mode_get_state() { + return mode_state; +} + +int filter_get_state() { + return filter_state; +} + +int noise_get_state() { + return noise_state; +} + +int agc_get_state() { + return agc_state; +} + +int mox_get_state() { + return mox_state; +} + +int lock_get_state() { + return lock_state; +} + + +static int vfo_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(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 <gtk/gtk.h> #include <gdk/gdk.h> #include <math.h> @@ -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;i<devices;i++) { d=&discovered[i]; - sprintf(text,"%s %s (%02X:%02X:%02X:%02X:%02X:%02X) on interface %s\n", + sprintf(text,"%s (%s %d.%d) %s (%02X:%02X:%02X:%02X:%02X:%02X) on %s\n", d->name, + 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 <sys/utsname.h> 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 <gtk/gtk.h> +#include <semaphore.h> +#include <stdio.h> + +#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;i<HAM_BANDS;i++) { + BAND *band=band_get_band(i); + + GtkWidget *band_label=gtk_label_new(band->title); + //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 <gtk/gtk.h> #include <gdk/gdk.h> #include <string.h> @@ -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 <stdlib.h> #include <stdio.h> #include <sys/types.h> @@ -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<samplesperframe;i++) { leftsample = (int)((signed char) buffer[b++]) << 16; @@ -906,7 +681,7 @@ fprintf(stderr,"outputsamples=%d\n", outputsamples); samples=0; } } - //} + } } else if(sourceport==COMMAND_RESPONCE_TO_HOST_PORT) { // command/response @@ -1033,14 +808,19 @@ fprintf(stderr,"outputsamples=%d\n", outputsamples); } if(running) { + sem_wait(&send_general_sem); if(send_general==1) { new_protocol_general(); send_general=0; } + sem_post(&send_general_sem); + + sem_wait(&send_high_priority_sem); if(send_high_priority==1) { new_protocol_high_priority(1,isTransmitting(),tune==0?drive:tune_drive); send_high_priority=0; } + sem_post(&send_high_priority_sem); } } diff --git a/new_protocol.h b/new_protocol.h index 1252f65..40ceee3 100644 --- a/new_protocol.h +++ b/new_protocol.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. +* +*/ + // port definitions from host #define GENERAL_REGISTERS_FROM_HOST_PORT 1024 @@ -17,19 +36,6 @@ #define BUFFER_SIZE 1024 -#define modeLSB 0 -#define modeUSB 1 -#define modeDSB 2 -#define modeCWL 3 -#define modeCWU 4 -#define modeFMN 5 -#define modeAM 6 -#define modeDIGU 7 -#define modeSPEC 8 -#define modeDIGL 9 -#define modeSAM 10 -#define modeDRM 11 - extern int data_socket; extern sem_t response_sem; @@ -40,6 +46,12 @@ extern unsigned int exciter_power; extern unsigned int alex_forward_power; extern unsigned int alex_reverse_power; +extern int send_high_priority; +extern int send_general; + +void schedule_high_priority(int source); +void schedule_general(); + void new_protocol_init(int rx,int pixels); void new_protocol_stop(); @@ -48,31 +60,9 @@ void pa_changed(); void tuner_changed(); void cw_changed(); -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); - void setMox(int state); int getMox(); void setTune(int state); int getTune(); int isTransmitting(); -double getDrive(); -void setDrive(double d); -double getTuneDrive(); -void setTuneDrive(double d); - -void setSampleRate(int rate); -int getSampleRate(); -void setFrequency(long long f); -long long getFrequency(); -void setMode(int m); -int getMode(); -void setFilter(int low,int high); -int getFilterLow(); -int getFilterHigh(); - diff --git a/new_protocol_programmer.c b/new_protocol_programmer.c index 8d0dea0..728970c 100644 --- a/new_protocol_programmer.c +++ b/new_protocol_programmer.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 <stdio.h> #include <stdlib.h> #include <string.h> 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 <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <net/if_arp.h> +#include <net/if.h> +#include <ifaddrs.h> +#include <pthread.h> +#include <string.h> +#include <errno.h> + +#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(devices<MAX_DEVICES) { + discovered[devices].protocol=ORIGINAL_PROTOCOL; + discovered[devices].device=buffer[10]&0xFF; + switch(discovered[devices].device) { + case DEVICE_METIS: + strcpy(discovered[devices].name,"Metis"); + break; + case DEVICE_HERMES: + strcpy(discovered[devices].name,"Hermes"); + break; + case DEVICE_GRIFFIN: + strcpy(discovered[devices].name,"Griffin"); + break; + case DEVICE_ANGELIA: + strcpy(discovered[devices].name,"Angelia"); + break; + case DEVICE_ORION: + strcpy(discovered[devices].name,"Orion"); + break; + case DEVICE_HERMES_LITE: + strcpy(discovered[devices].name,"Hermes Lite"); + break; + default: + strcpy(discovered[devices].name,"Unknown"); + break; + } + discovered[devices].software_version=buffer[9]&0xFF; + for(i=0;i<6;i++) { + discovered[devices].mac_address[i]=buffer[i+3]; + } + discovered[devices].status=status; + memcpy((void*)&discovered[devices].address,(void*)&addr,sizeof(addr)); + discovered[devices].address_length=sizeof(addr); + memcpy((void*)&discovered[devices].interface_address,(void*)&interface_addr,sizeof(interface_addr)); + discovered[devices].interface_length=sizeof(interface_addr); + strcpy(discovered[devices].interface_name,interface_name); + fprintf(stderr,"discovery: found device=%d software_version=%d status=%d address=%s (%02X:%02X:%02X:%02X:%02X:%02X) on %s\n", + discovered[devices].device, + discovered[devices].software_version, + discovered[devices].status, + inet_ntoa(discovered[devices].address.sin_addr), + discovered[devices].mac_address[0], + discovered[devices].mac_address[1], + discovered[devices].mac_address[2], + discovered[devices].mac_address[3], + discovered[devices].mac_address[4], + discovered[devices].mac_address[5], + discovered[devices].interface_name); + devices++; + } + } + } + + } + fprintf(stderr,"discovery: exiting discover_receive_thread\n"); + pthread_exit(NULL); +} + +void old_discovery() { + struct ifaddrs *addrs,*ifa; + +fprintf(stderr,"old_discovery\n"); + 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 + && (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<devices;i++) { + fprintf(stderr,"discovery: found device=%d software_version=%d status=%d address=%s (%02X:%02X:%02X:%02X:%02X:%02X) on %s\n", + discovered[i].device, + discovered[i].software_version, + discovered[i].status, + inet_ntoa(discovered[i].address.sin_addr), + discovered[i].mac_address[0], + discovered[i].mac_address[1], + discovered[i].mac_address[2], + discovered[i].mac_address[3], + discovered[i].mac_address[4], + discovered[i].mac_address[5], + discovered[i].interface_name); + } + +} + diff --git a/old_discovery.h b/old_discovery.h new file mode 100644 index 0000000..89fddbe --- /dev/null +++ b/old_discovery.h @@ -0,0 +1,20 @@ +/* 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 old_discovery(void); diff --git a/old_protocol.c b/old_protocol.c new file mode 100644 index 0000000..9be6c60 --- /dev/null +++ b/old_protocol.c @@ -0,0 +1,803 @@ +/* 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 <gtk/gtk.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <net/if_arp.h> +#include <net/if.h> +#include <ifaddrs.h> +#include <pthread.h> +#include <semaphore.h> +#include <string.h> +#include <errno.h> +#include <math.h> + +#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;i<OZY_BUFFER_SIZE;i++) { + output_buffer[i]=0; + } + do { + ozy_send_buffer(); + } while (command!=0); + + metis_start_stop(1); + +} + +static void start_receive_thread() { + int i; + int rc; + struct hostent *h; + + fprintf(stderr,"old_protocol starting receive thread\n"); + + DISCOVERED* d=&discovered[selected_device]; + + data_socket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); + if(data_socket<0) { + perror("old_protocol: create socket failed for data_socket\n"); + exit(-1); + } + + int optval = 1; + setsockopt(data_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + // bind to the interface + if(bind(data_socket,(struct sockaddr*)&d->interface_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<output_buffer_size;j++) { + left_rx_sample=0; + right_rx_sample=0; + left_tx_sample=(short)(micoutputbuffer[j*2]*gain*2); + right_tx_sample=(short)(micoutputbuffer[(j*2)+1]*gain*2); + output_buffer[output_buffer_index++]=left_rx_sample>>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<output_buffer_size;j++) { + left_rx_sample=(short)(audiooutputbuffer[j*2]*32767.0*volume); + right_rx_sample=(short)(audiooutputbuffer[(j*2)+1]*32767.0*volume); + left_tx_sample=0; + right_tx_sample=0; + output_buffer[output_buffer_index++]=left_rx_sample>>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 <gtk/gtk.h> #include <gdk/gdk.h> #include <math.h> @@ -5,7 +24,9 @@ #include <stdlib.h> #include <string.h> #include <semaphore.h> -#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<display_width;i++) { - f = getFrequency() - ((long) getSampleRate() / 2) + (long) (hz_per_pixel * i); + f = getFrequency() - half + (long) (hz_per_pixel * i); if (f > 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_display<bandLimits->minFrequency)&&(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_display<bandLimits->maxFrequency)&&(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;i<display_width;i++) { s1=samples[i-1]+get_attenuation(); - s1 = floor((panadapter_max - s1) + s1 = floor((panadapter_high - s1) * (double) panadapter_height - / (panadapter_max - panadapter_min)); + / (panadapter_high - panadapter_low)); s2=samples[i]+get_attenuation(); - s2 = floor((panadapter_max - s2) + s2 = floor((panadapter_high - s2) * (double) panadapter_height - / (panadapter_max - panadapter_min)); + / (panadapter_high - panadapter_low)); cairo_move_to(cr, (double)i-1, s1); cairo_line_to(cr, (double)i, s2); } @@ -260,8 +305,8 @@ void panadapter_update(float *data,int tx) { gtk_widget_queue_draw (panadapter); if(tx) { - panadapter_max=saved_max; - panadapter_min=saved_min; + panadapter_high=saved_max; + panadapter_low=saved_min; hz_per_pixel=saved_hz_per_pixel; } diff --git a/panadapter.h b/panadapter.h index 142ff31..278f504 100644 --- a/panadapter.h +++ b/panadapter.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 panadapter_update(float* data,int tx); GtkWidget* panadapter_init(int width,int height); diff --git a/pihpsdr b/pihpsdr deleted file mode 100755 index f647db0816bcc7e697a8978294d86918f5270609..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 200890 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 C?OY20 diff --git a/property.c b/property.c index 22bf486..32a7f07 100644 --- a/property.c +++ b/property.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 <stdlib.h> #include <stdio.h> #include <string.h> 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 <stdlib.h> #include <stdio.h> #include <string.h> +#include <semaphore.h> #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 <stdio.h> -#include <pigpio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <fcntl.h> -#include <poll.h> -#include <sched.h> -#include <pthread.h> -#include <wiringPi.h> - -#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 <gtk/gtk.h> +#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 <gtk/gtk.h> #include <semaphore.h> #include <stdio.h> @@ -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;i<FILTERS;i++) { FILTER* band_filter=&band_filters[i]; GtkWidget *b=gtk_button_new_with_label(band_filters[i].title); - 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==entry->filter) { 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 <gtk/gtk.h> #include <gdk/gdk.h> #include <math.h> @@ -7,32 +26,29 @@ #include <stdlib.h> #include <unistd.h> +#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 <gtk/gtk.h> #include <gdk/gdk.h> @@ -5,16 +24,13 @@ #include <unistd.h> #include <semaphore.h> #include <string.h> +#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;i<width;i++) { sample=data[i]+get_attenuation(); average+=(int)sample; - if(sample<lowThreshold) { + if(sample<(float)waterfall_low) { *p++=colorLowR; *p++=colorLowG; *p++=colorLowB; - } else if(sample>highThreshold) { + } 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 <gtk/gtk.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <net/if_arp.h> +#include <net/if.h> +#include <ifaddrs.h> +#include <pthread.h> +#include <semaphore.h> +#include <math.h> + +#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