From 27ab301e99d3336250ca92f431a33026e5ff8ffe Mon Sep 17 00:00:00 2001
From: c vw <dl1ycf@darc.de>
Date: Tue, 23 Apr 2019 19:26:29 +0200
Subject: [PATCH] MIDI support, and some corrections

---
 Makefile.mac    |  29 ++--
 ext.c           |  63 +++++++--
 ext.h           |  12 ++
 gpio.c          |   2 +-
 hpsdrsim.c      | 360 +++++++++++++++++++++++++++---------------------
 mac_midi.c      | 142 +++++++++++++++++++
 midi.h          | 192 ++++++++++++++++++++++++++
 midi.inp        |  33 +++++
 midi2.c         | 257 ++++++++++++++++++++++++++++++++++
 midi3.c         | 233 +++++++++++++++++++++++++++++++
 old_discovery.c |   4 +
 ps_menu.c       |   8 +-
 radio.c         |   8 ++
 rigctl.c        |   5 +-
 sliders.c       |  18 ++-
 sliders.h       |   2 +-
 16 files changed, 1177 insertions(+), 191 deletions(-)
 create mode 100644 mac_midi.c
 create mode 100644 midi.h
 create mode 100644 midi.inp
 create mode 100644 midi2.c
 create mode 100644 midi3.c

diff --git a/Makefile.mac b/Makefile.mac
index cc5a7c8..45aa79f 100644
--- a/Makefile.mac
+++ b/Makefile.mac
@@ -36,6 +36,9 @@ STEMLAB_FIX_OPTION=-DSTEMLAB_FIX
 # uncomment the line below to include support for Pi SDR
 #PI_SDR_INCLUDE=PI_SDR
 
+# uncomment the line below to include MIDI support
+MIDI_INCLUDE=MIDI
+
 #uncomment the line below for the platform being compiled on (actually not used)
 UNAME_N=raspberrypi
 #UNAME_N=odroid
@@ -49,6 +52,14 @@ UNAME_N=raspberrypi
 CC=gcc
 LINK=gcc
 
+ifeq ($(MIDI_INCLUDE),MIDI)
+MIDI_OPTIONS=-D MIDI
+MIDI_SOURCES= mac_midi.c midi2.c midi3.c
+MIDI_HEADERS= midi.h
+MIDI_OBJS= mac_midi.o midi2.o midi3.o
+MIDI_LIBS= -framework CoreMIDI -framework Foundation
+endif
+
 ifeq ($(PURESIGNAL_INCLUDE),PURESIGNAL)
 PURESIGNAL_OPTIONS=-D PURESIGNAL
 PURESIGNAL_SOURCES= \
@@ -187,11 +198,11 @@ GTKLIBS=`pkg-config --libs gtk+-3.0`
 PORTAUDIO_OPTIONS=-DPORTAUDIO
 AUDIO_LIBS=-lportaudio
 
-OPTIONS=-g -Wno-deprecated-declarations $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(USBOZY_OPTIONS) $(I2C_OPTIONS) $(GPIO_OPTIONS) $(LIMESDR_OPTIONS) \
+OPTIONS=-g -Wno-deprecated-declarations $(MIDI_OPTIONS) $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(USBOZY_OPTIONS) $(I2C_OPTIONS) $(GPIO_OPTIONS) $(LIMESDR_OPTIONS) \
                 $(FREEDV_OPTIONS) $(LOCALCW_OPTIONS) $(RADIOBERRY_OPTIONS) $(PI_SDR_OPTIONS) $(PSK_OPTIONS) $(STEMLAB_OPTIONS) $(STEMLAB_FIX_OPTION) \
                 -D GIT_DATE='"$(GIT_DATE)"' -D GIT_VERSION='"$(GIT_VERSION)"' $(PORTAUDIO_OPTIONS) $(DEBUG_OPTION) -O3
 
-LIBS=-lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(PSKLIBS) $(GTKLIBS) $(GPIO_LIBS) $(SOAPYSDRLIBS) $(FREEDVLIBS) $(STEMLAB_LIBS)
+LIBS=-lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(PSKLIBS) $(GTKLIBS) $(GPIO_LIBS) $(SOAPYSDRLIBS) $(FREEDVLIBS) $(STEMLAB_LIBS) $(MIDI_LIBS)
 INCLUDES=$(GTKINCLUDES)
 
 COMPILE=$(CC) $(OPTIONS) $(INCLUDES)
@@ -413,10 +424,10 @@ error_handler.o \
 cwramp.o \
 portaudio.o
 
-$(PROGRAM):  $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS)
-	$(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) $(LIBS)
+$(PROGRAM):  $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(MIDI_OBJS) $(STEMLAB_OBJS)
+	$(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(MIDI_OBJS) $(STEMLAB_OBJS) $(LIBS)
 
-all: prebuild  $(PROGRAM) $(HEADERS) $(REMOTE_HEADERS) $(USBOZY_HEADERS) $(LIMESDR_HEADERS) $(FREEDV_HEADERS) $(LOCALCW_HEADERS) $(I2C_HEADERS) $(GPIO_HEADERS) $(PSK_HEADERS) $(PURESIGNAL_HEADERS) $(STEMLAB_HEADERS) $(SOURCES) $(REMOTE_SOURCES) $(USBOZY_SOURCES) $(LIMESDR_SOURCES) $(FREEDV_SOURCES) $(I2C_SOURCES) $(GPIO_SOURCES) $(PSK_SOURCES) $(PURESIGNAL_SOURCES) $(STEMLAB_SOURCES)
+all: prebuild  $(PROGRAM) $(HEADERS) $(REMOTE_HEADERS) $(USBOZY_HEADERS) $(LIMESDR_HEADERS) $(FREEDV_HEADERS) $(LOCALCW_HEADERS) $(I2C_HEADERS) $(GPIO_HEADERS) $(PSK_HEADERS) $(PURESIGNAL_HEADERS) $(MIDI_HEADERS) $(STEMLAB_HEADERS) $(SOURCES) $(REMOTE_SOURCES) $(USBOZY_SOURCES) $(LIMESDR_SOURCES) $(FREEDV_SOURCES) $(I2C_SOURCES) $(GPIO_SOURCES) $(PSK_SOURCES) $(PURESIGNAL_SOURCES) $(MIDI_SOURCES) $(STEMLAB_SOURCES)
 
 prebuild:
 	rm -f version.o
@@ -474,11 +485,11 @@ hpsdrsim:       hpsdrsim.o
 #############################################################################
 app:	$(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) \
 		$(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) \
-		$(PURESIGNAL_OBJS) $(STEMLAB_OBJS)
+		$(PURESIGNAL_OBJS) $(MIDI_OBJS) $(STEMLAB_OBJS)
 	$(LINK) -headerpad_max_install_names -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) \
 		$(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) \
 		$(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) \
-		$(STEMLAB_OBJS) $(LIBS)
+		$(MIDI_OBJS) $(STEMLAB_OBJS) $(LIBS)
 	@rm -rf pihpsdr.app
 	@mkdir -p pihpsdr.app/Contents/MacOS
 	@mkdir -p pihpsdr.app/Contents/Frameworks
@@ -497,11 +508,13 @@ app:	$(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) \
 	  install_name_tool -change "$$lib" "@executable_path/../Frameworks/$$libfn" pihpsdr.app/Contents/MacOS/pihpsdr-bin; \
 	done
 #
-# Make "app" and copy wdspWisdom and props file to app bundle
+# Make "app" and copy local files app bundle
 #
 localapp:	app
 	cp wdspWisdom pihpsdr.app/Contents/Resources
 	cp *.props    pihpsdr.app/Contents/Resources
+	cp midi.inp   pihpsdr.app/Contents/Resources
+	cp ip.addr    pihpsdr.app/Contents/Resources
 
 #############################################################################
 
diff --git a/ext.c b/ext.c
index 6550d12..0838bcb 100644
--- a/ext.c
+++ b/ext.c
@@ -60,7 +60,7 @@ int ext_vfo_update(void *data) {
 }
 
 int ext_vfo_filter_changed(void *data) {
-  vfo_filter_changed((uintptr_t)data);
+  vfo_filter_changed((int)(uintptr_t)data);
   return 0;
 }
 
@@ -75,39 +75,36 @@ int ext_band_update(void *data) {
 
 int ext_mode_update(void *data) {
   start_mode();
-  // DL1YCF added return statement
   return 0;
 }
 
 int ext_filter_update(void *data) {
   start_filter();
-  // DL1YCF added return statement
   return 0;
 }
 
 int ext_noise_update(void *data) {
   start_noise();
-  // DL1YCF added return statement
   return 0;
 }
 
 int ext_ptt_update(void *data) {
-  ptt_update((uintptr_t)data);
+  ptt_update((int)(uintptr_t)data);
   return 0;
 }
 
 int ext_mox_update(void *data) {
-  mox_update((uintptr_t)data);
+  mox_update((int)(uintptr_t)data);
   return 0;
 }
 
 int ext_tune_update(void *data) {
-  tune_update((uintptr_t)data);
+  tune_update((int)(uintptr_t)data);
   return 0;
 }
 
 int ext_vox_changed(void *data) {
-  vox_changed((uintptr_t)data);
+  vox_changed((int)(uintptr_t)data);
   return 0;
 }
 
@@ -128,17 +125,17 @@ int ext_calc_drive_level(void *data) {
 }
 
 int ext_vfo_band_changed(void *data) {
-  vfo_band_changed((uintptr_t)data);
+  vfo_band_changed((int)(uintptr_t)data);
   return 0;
 }
 
 int ext_radio_change_sample_rate(void *data) {
-  radio_change_sample_rate((uintptr_t)data);
+  radio_change_sample_rate((int)(uintptr_t)data);
   return 0;
 }
 
 int ext_update_squelch(void *data) {
-  set_squelch(active_receiver);
+  set_squelch();
   return 0;
 }
 
@@ -160,3 +157,47 @@ int ext_vfo_step(void *data) {
   vfo_step(step);
   return 0;
 }
+
+int ext_set_mic_gain(void * data) {
+  double d=*(double *)data;
+  set_mic_gain(d);
+  free(data);
+  return 0;
+}
+
+int ext_set_agc_gain(void *data) {
+  double d=*(double *)data;
+  set_agc_gain(d);
+  free(data);
+  return 0;
+}
+ 
+int ext_set_drive(void *data) {
+  double d=*(double *)data;
+  set_drive(d);
+  free(data);
+  return 0;
+}
+
+int ext_vfo_a_swap_b(void *data) {
+  vfo_a_swap_b();
+  return 0;
+}
+
+int ext_update_att_preamp(void *data) {
+  update_att_preamp();
+  return 0;
+}
+
+int ext_set_alex_attenuation(void *data) {
+  int val=(int)(uintptr_t)data;
+  set_alex_attenuation(val);
+  return 0;
+}
+
+int ext_set_attenuation_value(void *data) {
+  double d=*(double *)data;
+  set_attenuation_value(d);
+  free(data);
+  return 0;
+}
diff --git a/ext.h b/ext.h
index 1b850db..070a215 100644
--- a/ext.h
+++ b/ext.h
@@ -18,7 +18,10 @@
 */
 
 
+//
 // The following calls functions can be called usig g_idle_add
+// Use these calls from within the rigclt daemon, or the GPIO or MIDI stuff
+//
 
 extern int ext_discovery(void *data);
 extern int ext_vfo_update(void *data);
@@ -52,5 +55,14 @@ extern int ext_noise_update(void *data);
 extern int ext_tx_set_ps(void *data);
 extern int ext_ps_twotone(void *data);
 #endif
+
 int ext_vfo_step(void *data);
 int ext_vfo_mode_changed(void *data);
+int ext_set_af_gain(void *data);
+int ext_set_mic_gain(void *data);
+int ext_set_agc_gain(void *data);
+int ext_set_drive(void *data);
+int ext_vfo_a_swap_b(void *data);
+int ext_update_att_preamp(void *data);
+int ext_set_alex_attenuation(void *data);
+int ext_set_attenuation_value(void *data);
diff --git a/gpio.c b/gpio.c
index b00a0c4..a34510d 100644
--- a/gpio.c
+++ b/gpio.c
@@ -1407,7 +1407,7 @@ static void encoder_changed(int action,int pos) {
         value=100.0;
       }
       active_receiver->squelch=value;
-      set_squelch(active_receiver);
+      set_squelch();
       break;
     case ENCODER_COMP:
       value=(double)transmitter->compressor_level;
diff --git a/hpsdrsim.c b/hpsdrsim.c
index 7f49b1d..7860577 100755
--- a/hpsdrsim.c
+++ b/hpsdrsim.c
@@ -19,17 +19,20 @@
  * RF3 respects the "TX drive" and "TX ATT" settings
  * RF4 is the TX signal multiplied with 0.4 (ignores TX_DRIVE and TX_ATT).
  *
- * We support 4 receivers, they see
- * RX1:  RF1
- * RX2:  RF2 while receiving, RF3 while transmitting/48000
- * RX3:  RF2 while receiving, RF3 while transmitting/48000
- * RX4:  RF3 
+ * Depending on the device type, the receivers see different signals
+ * (This is necessary for PURESIGNAL). We chose the association such that
+ * it works both with and without PURESIGNAL. Upon receiving, the association
+ * is as follows:
+ * RX1=RF1, RX2=RF2, RX3=RF2, 
  *
- * RX5 to RX8: no signal
- * 
- * This is the setting for PURESIGNAL with HERMES boards.
- * To simulate Orion2 boards (Anan7000 etc), we should connect RX4 with RF2 and RX5 with RF3.
+ * The connection upon transmitting depends on the DEVICE, namely
+ *
+ * DEVICE=METIS:    RX1=RF3, RX2=RF4
+ * DEVICE=HERMES:   RX3=RF3, RX4=RF4  (also for DEVICE=StemLab)
+ * DEVICE=ORION2:   RX4=RF3, RX5=RF5  (also for DEVICE=ANGELIA and ORION)
  *
+ * Note that currently, RF3 and RF4 only exist when using 48000 Hz sample rate. 
+ * 
  */
 #include <stdio.h>
 #include <errno.h>
@@ -66,75 +69,75 @@ static int sock_TCP_Client = -1;
  * Whenevery they are changed, this is reported.
  */
 
-int		AlexTXrel = -1;
-int		alexRXout = -1;
-int		alexRXant = -1;
-int		MicTS = -1;
-int		duplex = -1;
-int		receivers = -1;
-int		rate = -1;
-int             preamp = -1;
-int		LTdither = -1;
-int		LTrandom = -1;
-int		AlexRXant = -1;
-int		AlexRXout = -1;
-int             ref10 = -1;
-int		src122 = -1;
-int		PMconfig = -1;
-int		MicSrc = -1;
-int		txdrive = 0;
-int		txatt = 0;
-int		sidetone_volume = -1;
-int		cw_internal = -1;
-int		rx_att[2] = {-1,-1};
-int		rx1_attE = -1;
-int             rx_preamp[4] = {-1,-1,-1,-1};
-int		MerTxATT0 = -1;
-int		MerTxATT1 = -1;
-int		MetisDB9 = -1;
-int		PeneSel = -1;
-int		PureSignal = -1;
-int		LineGain = -1;
-int		MicPTT = -1;
-int		tip_ring = -1;
-int		MicBias = -1;
-int		ptt=-1;
-int		att=-1;
-int		TX_class_E = -1;
-int		OpenCollectorOutputs=-1;
-long		tx_freq=-1;
-long		rx_freq[7] = {-1,-1,-1,-1,-1,-1,-1};
-int		hermes_config=-1;
-int		alex_lpf=-1;
-int		alex_hpf=-1;
-int		c25_ext_board_i2c_data=-1;
-int		rx_adc[7]={0,1,1,2,-1,-1,-1};
-int		cw_hang = -1;
-int		cw_reversed = -1;
-int		cw_speed = -1;
-int		cw_mode = -1;
-int		cw_weight = -1;
-int		cw_spacing = -1;
-int		cw_delay = -1;
-int		CommonMercuryFreq = -1;
-int             freq=-1;
+static int		AlexTXrel = -1;
+static int		alexRXout = -1;
+static int		alexRXant = -1;
+static int		MicTS = -1;
+static int		duplex = -1;
+static int		receivers = -1;
+static int		rate = -1;
+static int             preamp = -1;
+static int		LTdither = -1;
+static int		LTrandom = -1;
+static int		AlexRXant = -1;
+static int		AlexRXout = -1;
+static int             ref10 = -1;
+static int		src122 = -1;
+static int		PMconfig = -1;
+static int		MicSrc = -1;
+static int		txdrive = 0;
+static int		txatt = 0;
+static int		sidetone_volume = -1;
+static int		cw_internal = -1;
+static int		rx_att[2] = {-1,-1};
+static int		rx1_attE = -1;
+static int              rx_preamp[4] = {-1,-1,-1,-1};
+static int		MerTxATT0 = -1;
+static int		MerTxATT1 = -1;
+static int		MetisDB9 = -1;
+static int		PeneSel = -1;
+static int		PureSignal = -1;
+static int		LineGain = -1;
+static int		MicPTT = -1;
+static int		tip_ring = -1;
+static int		MicBias = -1;
+static int		ptt=-1;
+static int		att=-1;
+static int		TX_class_E = -1;
+static int		OpenCollectorOutputs=-1;
+static long		tx_freq=-1;
+static long		rx_freq[7] = {-1,-1,-1,-1,-1,-1,-1};
+static int		hermes_config=-1;
+static int		alex_lpf=-1;
+static int		alex_hpf=-1;
+static int		c25_ext_board_i2c_data=-1;
+static int		rx_adc[7]={0,1,1,2,-1,-1,-1};
+static int		cw_hang = -1;
+static int		cw_reversed = -1;
+static int		cw_speed = -1;
+static int		cw_mode = -1;
+static int		cw_weight = -1;
+static int		cw_spacing = -1;
+static int		cw_delay = -1;
+static int		CommonMercuryFreq = -1;
+static int             freq=-1;
 
 
 // floating-point represeners of TX att, RX att, and RX preamp settings
 
-double txdrv_dbl = 1.0;
-double txatt_dbl = 1.0;
-double rxatt_dbl[4] = {1.0, 1.0, 1.0, 1.0};   // this reflects both ATT and PREAMP
+static double txdrv_dbl = 1.0;
+static double txatt_dbl = 1.0;
+static double rxatt_dbl[4] = {1.0, 1.0, 1.0, 1.0};   // this reflects both ATT and PREAMP
 
-int sock_ep2;
+static int sock_ep2;
 
-struct sockaddr_in addr_ep6;
+static struct sockaddr_in addr_ep6;
 
 /*
  * These two variables monitor whether the TX thread is active
  */
-int enable_thread = 0;
-int active_thread = 0;
+static int enable_thread = 0;
+static int active_thread = 0;
 
 void process_ep2(uint8_t *frame);
 void *handler_ep6(void *arg);
@@ -146,10 +149,12 @@ void *handler_ep6(void *arg);
 
 // 63 * 130,  RTXLEN must be an even multiple of 63!
 #define RTXLEN 8190
-double  isample[RTXLEN];
-double  qsample[RTXLEN];
-int  txptr=0;
-int  rxptr=0;
+static double  isample[RTXLEN];
+static double  qsample[RTXLEN];
+static int  txptr=0;
+static int  rxptr=0;
+
+static int ismetis,isorion,ishermes,isc25;
 
 int main(int argc, char *argv[])
 {
@@ -157,6 +162,7 @@ int main(int argc, char *argv[])
 	struct sched_param param;
 	pthread_attr_t attr;
 	pthread_t thread;
+        int DEVICE;
 
 	uint8_t reply[11] = { 0xef, 0xfe, 2, 0, 0, 0, 0, 0, 0, 32, 1 };
 
@@ -179,6 +185,31 @@ int main(int argc, char *argv[])
 	int bytes_read, bytes_left;
 	uint32_t *code0 = (uint32_t *) buffer;  // fast access to code of first buffer
 
+/*
+ *      Examples for METIS:	ANAN10E, ANAN100B
+ *      Examples for HERMES:	HERMES, ANAN10, ANAN100
+ *	Examples for ORION:	ANAN100D, ANAN200D
+ *	Examples for ORION2:	ANAN7000D, ANAN8000D
+ */
+
+	DEVICE=1; // default is Hermes
+        if (argc > 1) {
+	    if (!strncmp(argv[1],"-metis"  ,6))  DEVICE=0;
+	    if (!strncmp(argv[1],"-hermes" ,7))  DEVICE=1;
+	    if (!strncmp(argv[1],"-orion" , 6))  DEVICE=5;
+	    if (!strncmp(argv[1],"-orion2" ,7))  DEVICE=10;   // Anan7000 in old protocol
+	    if (!strncmp(argv[1],"-c25"    ,8))  DEVICE=100;  // the same as hermes
+        }
+	ismetis=ishermes=isorion=isc25;
+	switch (DEVICE) {
+	    case   0: fprintf(stderr,"DEVICE is METIS\n");   ismetis=1;  break;
+	    case   1: fprintf(stderr,"DEVICE is HERMES\n");  ishermes=1; break;
+	    case   5: fprintf(stderr,"DEVICE is ORION\n");   isorion=1;  break;
+	    case  10: fprintf(stderr,"DEVICE is ORION2\n");  isorion=1;  break;
+	    case 100: fprintf(stderr,"DEVICE is StemLab\n"); ishermes=1;  isc25=1; break;
+	}
+	reply[10]=DEVICE;
+
 	if ((sock_ep2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
 	{
 		perror("socket");
@@ -422,11 +453,6 @@ int main(int argc, char *argv[])
 				memset(buffer, 0, 60);
 				memcpy(buffer, reply, 11);
 
-				// ab-use some of the "unused" bytes in the reply block to indicate we can do TCP
-				// This information can be used by SDR programs
-				buffer[11]='T';
-				buffer[12]='C';
-				buffer[13]='P';
 				if (sock_TCP_Client > -1)
 				{
 					// We will get into trouble if we respond via TCP while the radio is
@@ -568,8 +594,8 @@ void process_ep2(uint8_t *frame)
 
           chk_data((frame[3] & 0x03) >> 0, att, "ALEX Attenuator");
           chk_data((frame[3] & 0x04) >> 2, preamp, "ALEX preamp");
-          chk_data((frame[3] & 0x08) >> 2, LTdither, "LT2208 Dither");
-          chk_data((frame[3] & 0x10) >> 2, LTrandom, "LT2208 Random");
+          chk_data((frame[3] & 0x08) >> 3, LTdither, "LT2208 Dither");
+          chk_data((frame[3] & 0x10) >> 4, LTrandom, "LT2208 Random");
           chk_data((frame[3] & 0x60) >> 5, alexRXant, "ALEX RX ant");
           chk_data((frame[3] & 0x80) >> 7, alexRXout, "ALEX RX out");
 
@@ -578,6 +604,13 @@ void process_ep2(uint8_t *frame)
           chk_data(((frame[4] >> 3) & 7) + 1, receivers, "RECEIVERS");
           chk_data(((frame[4] >> 6) & 1), MicTS, "TimeStampMic");
           chk_data(((frame[4] >> 7) & 1), CommonMercuryFreq,"Common Mercury Freq");
+
+	  if (isc25) {
+             // Charly25: has two 18-dB preamps that are switched with "preamp" and "dither"
+             //           and a step-attenuator triggered by Alex ATT ONLY RX1!
+               rxatt_dbl[0]=pow(10.0, -0.05*(12*att-18*LTdither-18*preamp));
+               rxatt_dbl[1]=1.0;
+          }
 	  break;
 
         case 2:
@@ -651,11 +684,13 @@ void process_ep2(uint8_t *frame)
    	   chk_data((frame[4] & 0x1F) >> 0, rx_att[0], "RX1 ATT");
    	   chk_data((frame[4] & 0x20) >> 5, rx1_attE, "RX1 ATT enable");
 
-	   // Set RX amplification factors. Assume 20 dB preamps
-           rxatt_dbl[0]=pow(10.0, -0.05*(rx_att[0]-20*rx_preamp[0]));
-           rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1]-20*rx_preamp[1]));
-           rxatt_dbl[2]=pow(10.0, (double) rx_preamp[2]);
-           rxatt_dbl[3]=pow(10.0, (double) rx_preamp[3]);
+	   if (!isc25) {
+	     // Set RX amplification factors. Assume 20 dB preamps
+             rxatt_dbl[0]=pow(10.0, -0.05*(rx_att[0]-20*rx_preamp[0]));
+             rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1]-20*rx_preamp[1]));
+             rxatt_dbl[2]=pow(10.0, (double) rx_preamp[2]);
+             rxatt_dbl[3]=pow(10.0, (double) rx_preamp[3]);
+	   }
 	   break;
 
 	case 22:
@@ -740,7 +775,13 @@ void *handler_ep6(void *arg)
 		127, 127, 127, 24, 0, 0, 0, 0,
 		127, 127, 127, 32, 66, 66, 66, 66
 	};
-        int32_t sample;
+        int32_t rf1isample,rf1qsample;
+        int32_t rf2isample,rf2qsample;
+        int32_t rf3isample,rf3qsample;
+        int32_t rf4isample,rf4qsample;
+
+	int32_t myisample,myqsample;
+
         struct timespec delay;
 #ifdef __APPLE__
 	struct timespec now;
@@ -834,80 +875,87 @@ void *handler_ep6(void *arg)
 		    pointer += 8;
 		    memset(pointer, 0, 504);
 		    for (j=0; j<n; j++) {
+			//
+			// Define samples of our sources RF1 through RF4
+			//
+			rf1isample= noiseItab[noiseIQpt] * 8388607.0;			// Noise
+			rf1isample += T0800Itab[pt0800] * 83.886070 *rxatt_dbl[0];	// tone 100 dB below peak
+			rf1qsample=noiseQtab[noiseIQpt] * 8388607.0;
+			rf1qsample += T0800Qtab[pt0800] * 83.886070 *rxatt_dbl[0];
+			//
+			rf2isample= noiseItab[noiseIQpt] * 8388607.0;			// Noise
+			rf2isample += T2000Itab[pt2000] * 838.86070 * rxatt_dbl[1];	// tone 80 dB below peak
+			rf2qsample=noiseQtab[noiseIQpt] * 8388607.0;
+			rf2qsample += T2000Qtab[pt2000] * 838.86070 * rxatt_dbl[1];
+			//
+			// RF3: TX signal distorted
+			//
+			i1=isample[rxptr]*txdrv_dbl;
+			q1=qsample[rxptr]*txdrv_dbl;
+			fac=IM3a+IM3b*(i1*i1+q1*q1);
+			rf3isample= txatt_dbl*i1*fac * 8388607.0;
+			rf3qsample= txatt_dbl*q1*fac * 8388607.0;
+			//
+			// RF4: TX signal with peak=0.4
+			//
+			rf4isample= isample[rxptr] * 0.400 * 8388607.0;
+			rf4qsample= qsample[rxptr] * 0.400 * 8388607.0;
+
+
+
 			for (k=0; k< receivers; k++) {
+			    myisample=0;
+			    myqsample=0;
 			    switch (k) {
-				case 0: // RX1 sees RF1
-				    //
-				    // RF1: noise + weak 800 Hz tone
-				    //
-				    sample= noiseItab[noiseIQpt] * 8388607.0;
-			  	    sample += T0800Itab[pt0800] * 83.886070 *rxatt_dbl[0];  // 100 dB below peak
-			   	    *pointer++ = (sample >> 16) & 0xFF;
-				    *pointer++ = (sample >>  8) & 0xFF;
-				    *pointer++ = (sample >>  0) & 0xFF;
-				    sample=noiseQtab[noiseIQpt] * 8388607.0;
-			  	    sample += T0800Qtab[pt0800] * 83.886070 *rxatt_dbl[0];  // 100 dB below peak
-				    *pointer++ = (sample >> 16) & 0xFF;
-				    *pointer++ = (sample >>  8) & 0xFF;
-				    *pointer++ = (sample >>  0) & 0xFF;
-				    break;
-			  	case 1: // RX2 and RX3 see RF2 upon receiving, RF3 upon transmitting
-				case 2:
-                                    if (rate == 0 && ptt) {
-                                    	//
-					// RF3:
-                                    	// Distorted (feed-back) TX signal
-                                    	// Note we first add distortion, then adjust level
-                                    	// Therefore we first multiply with txdrv_dbl, then
-                                    	// distort, and then attenuate with txatt_dbl.
-                                   	//
-                                        i1=isample[rxptr]*txdrv_dbl;
-                                        q1=qsample[rxptr]*txdrv_dbl;
-                                        fac=IM3a+IM3b*(i1*i1+q1*q1);
-                                        sample= txatt_dbl*i1*fac * 8388607.0;
-                                        *pointer++ = (sample >> 16) & 0xFF;
-                                        *pointer++ = (sample >>  8) & 0xFF;
-                                        *pointer++ = (sample >>  0) & 0xFF;
-                                        sample= txatt_dbl*q1*fac * 8388607.0;
-                                        *pointer++ = (sample >> 16) & 0xFF;
-                                        *pointer++ = (sample >>  8) & 0xFF;
-                                        *pointer++ = (sample >>  0) & 0xFF;
-                                    } else {
-				    	//
-				    	// RF2: noise + weak 2000 Hz tone
-				    	//
-				    	sample= noiseItab[noiseIQpt] * 8388607.0;
-			  	    	sample += T2000Itab[pt2000] * 838.86070 * rxatt_dbl[1];  // 80 dB below peak
-				    	*pointer++ = (sample >> 16) & 0xFF;
-				    	*pointer++ = (sample >>  8) & 0xFF;
-				    	*pointer++ = (sample >>  0) & 0xFF;
-				    	sample=noiseQtab[noiseIQpt] * 8388607.0;
-			  	    	sample += T2000Qtab[pt2000] * 838.86070 * rxatt_dbl[1];  // 80 dB below peak
-				    	*pointer++ = (sample >> 16) & 0xFF;
-				    	*pointer++ = (sample >>  8) & 0xFF;
-				    	*pointer++ = (sample >>  0) & 0xFF;
-				    }
-				    break;
-				case 3: // RX4 sees TX outgoing signal (no distortion, no attenuation)
-				    //
-				    // RF4: TX signal with HWPeak = 0.4
-				    //
-                        	    if (rate == 0 && ptt) {
-                                  	sample= isample[rxptr] * 0.400 * 8388607.0;
-                                  	*pointer++ = (sample >> 16) & 0xFF;
-                                  	*pointer++ = (sample >>  8) & 0xFF;
-                                  	*pointer++ = (sample >>  0) & 0xFF;
-                                  	sample= qsample[rxptr] * 0.400 * 8388607.0;
-                                  	*pointer++ = (sample >> 16) & 0xFF;
-                                  	*pointer++ = (sample >>  8) & 0xFF;
-                                  	*pointer++ = (sample >>  0) & 0xFF;
-                               	    } else {
-				  	pointer +=6;
-				    }
-				    break;
-				default:
-				    pointer +=6;
+			      case 0: // RX1
+				if (rate == 0 && ptt && ismetis) {
+				    myisample=rf3isample;
+				    myqsample=rf3qsample;
+				} else {
+				    myisample=rf1isample;
+				    myqsample=rf1qsample;
+				}
+				break;
+			      case 1: // RX2
+				if (rate == 0 && ptt && ismetis) {
+				    myisample=rf4isample;
+				    myqsample=rf4qsample;
+				} else {
+				    myisample=rf2isample;
+				    myqsample=rf2qsample;
+				}
+				break;
+			      case 2:
+                                if (rate == 0 && ptt && ishermes) {
+				    myisample=rf3isample;
+				    myqsample=rf3qsample;
+                                } else {
+				    myisample=rf2isample;
+				    myqsample=rf2qsample;
+				}
+				break;
+			      case 3: // RX4
+                        	if (rate == 0 && ptt && ishermes) {
+				    myisample=rf4isample;
+				    myqsample=rf4qsample;
+                               	} else if (rate == 0 && ptt && isorion) {
+				    myisample=rf3isample;
+				    myqsample=rf3qsample;
+				}
+				break;
+			      case 4: // RX5
+                               	if (rate == 0 && ptt && isorion) {
+				    myisample=rf4isample;
+				    myqsample=rf4qsample;
+				}
+				break;
 			    }
+			    *pointer++ = (myisample >> 16) & 0xFF;
+			    *pointer++ = (myisample >>  8) & 0xFF;
+			    *pointer++ = (myisample >>  0) & 0xFF;
+			    *pointer++ = (myqsample >> 16) & 0xFF;
+			    *pointer++ = (myqsample >>  8) & 0xFF;
+			    *pointer++ = (myqsample >>  0) & 0xFF;
 		        }
 			// Microphone samples: silence
 			pointer += 2;
diff --git a/mac_midi.c b/mac_midi.c
new file mode 100644
index 0000000..ad9885b
--- /dev/null
+++ b/mac_midi.c
@@ -0,0 +1,142 @@
+/*
+ * MIDI support for pihpsdr
+ * (C) Christoph van Wullen, DL1YCF.
+ *
+ * This is the "Layer-1" for Apple Macintosh.
+ *
+ * This file implements the function register_midi_device,
+ * which is called with the name of a supported MIDI device and
+ * the number of characters in the name that must be equal to
+ * the name of the MIDI device as known in the operating system.
+ *
+ * For example, the call
+ * register_midi_device("COMPANY MIDI X",13)
+ * will also use a MIDI device named "COMPANY MIDI Y".
+ *
+ * This file must generate calls to Layer-2 NewMidiEvent().
+ * Some type of messages are not consideres (pressure change, etc.),
+ * they are silently dropped but must be processed.
+ *
+ */
+
+#include "midi.h"
+
+#ifdef __APPLE__
+
+#include <Carbon/Carbon.h>
+
+#include <CoreMIDI/MIDIServices.h>
+#include <CoreAudio/HostTime.h>
+#include <CoreAudio/CoreAudio.h>
+
+//
+// MIDI callback function
+//
+static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {
+    int i,j,k,command,chan;
+    MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
+	
+	
+    // loop through all packets in the current list
+    for (j=0; j < pktlist->numPackets; ++j) {
+        if (packet->length > 0) { 
+	    for ( i = 0; i<(packet->length); ) {
+		command=packet->data[i];
+                if ((command & 128) != 128) continue;
+                
+                chan = command & 0x0F;
+
+                switch (command & 0xF0) {
+                  case 0x80:  // Note off
+			NewMidiEvent(MIDI_NOTE, chan, packet->data[i+1], 0);
+			fprintf(stderr,"NOTE OFF: Note=%d Chan=%d Vel=%d\n", packet->data[i+1], chan, packet->data[i+2]);
+			i +=3;
+			break;
+                  case 0x90:  // Note on
+			NewMidiEvent(MIDI_NOTE, chan, packet->data[i+1], 1);
+			fprintf(stderr,"NOTE ON : Note=%d Chan=%d Vel=%d\n", packet->data[i+1], chan, packet->data[i+2]);
+			i +=3;
+			break;
+                  case 0xA0:  // Polyph. Press.
+			fprintf(stderr,"PolPress: Note=%d Chan=%d Prs=%d\n", packet->data[i+1], chan, packet->data[i+2]);
+			i +=3;
+			break;
+                  case 0xB0:  // Control change
+			NewMidiEvent(MIDI_CTRL, chan, packet->data[i+1], packet->data[i+2]);
+			fprintf(stderr,"CtlChang: Ctrl=%d Chan=%d Val=%d\n", packet->data[i+1], chan, packet->data[i+2]);
+			i +=3;
+			break;
+                  case 0xC0:  // Program change
+			fprintf(stderr,"PgmChang: Prog=%d Chan=%d\n", packet->data[i+1], chan);
+			i +=2;
+			break;
+                  case 0xD0:  // Channel Pressure
+			fprintf(stderr, "ChanPres: Pres=%d Chan=%d\n", packet->data[i+1], chan);
+			i +=2;
+			break;
+                  case 0xE0:  // Pitch Bend
+			NewMidiEvent(MIDI_PITCH, chan, 0, packet->data[i+1] + 128*packet->data[i+2]);
+			fprintf(stderr,"Pitch   : val =%d Chan=%d\n", packet->data[i+1] + 128*packet->data[i+2], chan);
+			i +=3;
+			break;
+                  case 0xF0:  
+			fprintf(stderr, "System  : %x", command);
+                        while ((command = (packet->data[++i]) & 128) != 128) {
+                          fprintf(stderr," %x",command);
+                        }
+                        fprintf(stderr,"\n");
+			break;
+                }
+	    } // i-loop through the packet
+	    packet = MIDIPacketNext(packet);
+        } // if packet length > 1
+    } // j-loop through the list of packets
+}
+
+
+void register_midi_device(char *myname) {
+    unsigned long nDevices;
+    int i;
+    CFStringRef pname;
+    char name[100];
+    int FoundMIDIref=-1;
+    int mylen=strlen(myname);
+
+
+//
+// Go through the list of MIDI devices and
+// look whether the one we are looking for is there
+//
+
+    nDevices=MIDIGetNumberOfSources();
+    for (i=0; i<nDevices; i++) {
+	MIDIEndpointRef dev = MIDIGetSource(i);
+	if (dev != 0) {
+	    MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &pname);
+	    CFStringGetCString(pname, name, sizeof(name), 0);
+	    CFRelease(pname);
+	    fprintf(stderr,"MIDI device found: >>>%s<<<\n", name);
+	    if (!strncmp(name, myname, mylen)) {
+		FoundMIDIref=i;
+		fprintf(stderr,"MIDI device found and selected: >>>%s<<<\n", name);
+	    } else {
+		fprintf(stderr,"MIDI device we were looking for   : >>>%s<<<\n", myname);
+		fprintf(stderr,"MIDI device found BUT NOT SELECTED: >>>%s<<<\n", name);
+	    }
+	}
+    }
+
+//
+// If we found "our" device, register a callback routine
+//
+
+    if (FoundMIDIref >= 0) {
+        MIDIClientRef client = 0;
+        MIDIPortRef myMIDIport = 0;
+        //Create client
+        MIDIClientCreate(CFSTR("piHPSDR"),NULL,NULL, &client);
+        MIDIInputPortCreate(client, CFSTR("FromMIDI"), ReadMIDIdevice, NULL, &myMIDIport);
+        MIDIPortConnectSource(myMIDIport,MIDIGetSource(FoundMIDIref), NULL);
+    }
+}
+#endif
diff --git a/midi.h b/midi.h
new file mode 100644
index 0000000..98a6191
--- /dev/null
+++ b/midi.h
@@ -0,0 +1,192 @@
+/*
+ * MIDI support for pihpsdr
+ *
+ * (C) Christoph van Wullen, DL1YCF.
+ *
+ * Midi support works in three layers
+ *
+ * Layer-1: hardware specific
+ * --------------------------
+ *
+ * Layer1 either implements a callback function (if the operating system
+ * supports MIDI) or a separate thread polling MIDI data. Whenever a
+ * MIDI command arrives, such as Note on/off or Midi-Controller value
+ * changed, it calls Layer 2.
+ *
+ * Layer-2: MIDI device specific
+ * ----------------------------- 
+ *
+ * Layer2 translates MIDI commands into pihpsdr actions. This is done with
+ * a table-driven algorithm, such that the same translator can be used for
+ * any MIDI device provided the tables have been set up correctly.
+ * It seems overly complicated to create a user interface for setting up
+ * these tables, instead a standard text file describing the MIDI device
+ * is read and the tables are set up.
+ * Layer-2 has SDR applications in mind, but is not necessarily specific
+ * to pihpsr. It calls the Layer-3 function.
+ *
+ * Layer-3: pihpsdr specific
+ * -------------------------
+ *
+ * Layer 3, finally, implements all the "actions" we can make, such as TUNE
+ * or VFO. This Layer calls pihpsdr functions.
+ *
+ * One word to MIDI channels. Usually, a MIDI device can be configured to use
+ * a specific channel, such that different keyboards use different channels.
+ * The Layer-2 tables can either specify that the MIDI command has to come from
+ * a specific channel, or can specify that the action will be taken not matter which
+ * channel the MIDI message comes from. The latter case should be the default, but
+ * if we want to connect more than one MIDI device, we need to speficy the channel.
+ *
+ * In principle this supports more than one MIDI device, but in this case they
+ * must generate MIDI events on different channels
+ */
+
+//
+// MIDIaction encodes the "action" to be taken in Layer3
+//
+enum MIDIaction {
+  ACTION_NONE=0,
+  VFO,
+  TUNE,
+  MOX,
+  AF_GAIN,
+  MIC_VOLUME,
+  TX_DRIVE,
+  ATT,
+  PRE,
+  AGC,
+  COMPRESS,
+  RIT_ONOFF,
+  RIT_VAL,
+  PAN_HIGH,
+  PAN_LOW,
+  BAND_UP,
+  BAND_DOWN,
+  FILTER_UP,
+  FILTER_DOWN,
+  MODE_UP,
+  MODE_DOWN,
+  SWAP_VFO
+};
+
+//
+// MIDItype encodes the type of MIDI control. This info
+// is passed from Layer-2 to Layer-3
+//
+// MIDI_KEY has no parameters and indicates that some
+// button has been pressed.
+//
+// MIDI_KNOB has a "value" parameter (between 0 and 100)
+// and indicates that some knob has been set to a specific
+// position.
+//
+// MIDI_WHEEL has a "direction" parameter and indicates that
+// some knob has been turned left/down or right/ip. The  value
+// can be
+//
+// -100 very fast going down
+//  -10 fast going down
+//   -1 going down
+//    1 going up
+//   10 fast going up
+//  100 very fast going up
+//
+
+enum MIDItype {
+ TYPE_NONE=0,
+ MIDI_KEY,          // Button (press event)
+ MIDI_KNOB,         // Knob   (value between 0 and 100)
+ MIDI_WHEEL         // Wheel  (direction and speed)
+};
+
+//
+// MIDIevent encodes the actual MIDI event "seen" in Layer-1 and
+// passed to Layer-2. MIDI_NOTE events end up as MIDI_KEY and
+// MIDI_PITCH as MIDI_KNOB, while MIDI_CTRL can end up both as
+// MIDI_KNOB or MIDI_WHEEL, depending on the device description.
+//
+enum MIDIevent {
+ EVENT_NONE=0,
+ MIDI_NOTE,
+ MIDI_CTRL,
+ MIDI_PITCH
+};
+
+//
+// Data structure for Layer-2
+//
+
+//
+// There is linked list of all specified MIDI events for a given "Note" value,
+// which contains the defined actions for all MIDI_NOTE and MIDI_CTRL events
+// with that given note and for all channels
+// Note on wheel delay:
+// If using a wheel for cycling through a menu, it is difficult to "hit" the correct
+// menu item if wheel events are generated at a very high rate. Therefore we can define
+// a delay: once a wheel event is reported upstream, any such events are suppressed during
+// the delay.
+// 
+// Note that with a MIDI KEY, you can choose that an action is
+// generated only for a NOTE_ON event or both for NOTE_ON and
+// NOTE_OFF. In the first case, if the key is associated to MOX,
+// then MOX is toggled each time the key is pressed. This behaves
+// very much like point-and-clicking the MOX buttion in the GUI.
+//
+// If an action is generated both on NOTE_ON and NOTE_OFF,
+// then MOX is engaged when pressing the key and disengaged
+// when releasing it. For MOX this makes little send but you
+// might want to configure the TUNE button this way.
+// The latter behaviour is triggered when the line assigning the key
+// or "NOTE OFF". The table speficying the behaviour of layer-2 thus
+// contains the key word "ONOFF". This is stored in the field "onoff"
+// in struct desc.
+
+struct desc {
+   int               channel;     // -1 for ANY channel
+   enum MIDIevent    event;	  // type of event (NOTE on/off, Controller change, Pitch value)
+   int               onoff;       // 1: generate upstream event both for Note-on and Note-off
+   enum MIDItype     type;        // Key, Knob, or Wheel
+   int               low_thr3;    // Wheel only: If controller value is <= this value, generate "very fast down"
+   int               low_thr2;    // Wheel only: If controller value is <= this value, generate "     fast down"
+   int               low_thr1;    // Wheel only: If controller value is <= this value, generate "          down"
+   int               up_thr1;     // Wheel only: If controller value is <= this value, generate "          up  "
+   int               up_thr2;     // Wheel only: If controller value is <= this value, generate "     fast up  "
+   int               up_thr3;     // Wheel only: If controller value is <= this value, generate "very fast up  "
+   int		     delay;       // Wheel only: delay (msec)
+   enum MIDIaction   action;	  // SDR "action" to generate
+   struct desc       *next;       // Next defined action for a controller/key with that note value.
+};
+
+struct {
+   struct desc *desc[128];    // description for Note On/Off and ControllerChange
+   struct desc *pitch;        // description for PitchChanges
+} MidiCommandsTable;
+
+//
+// Layer-1 entry point, called once for all the MIDI devices
+// that have been defined. This is called upon startup by
+// Layer-2 through the function MIDIstartup.
+//
+void register_midi_device(char *name);
+
+//
+// Layer-2 entry point (called by Layer1)
+// 
+// When Layer-1 has received a MIDI message, it calls
+// NewMidiEvent.
+//
+// MIDIstartup looks for files containing descriptions for MIDI
+// devices and calls the Layer-1 function register_midi_device
+// for each device description that was successfully read.
+
+void NewMidiEvent(enum MIDIevent event, int channel, int note, int val);
+void MIDIstartup();
+
+//
+// Layer-3 entry point (called by Layer2). In Layer-3, all the pihpsdr
+// actions (such as changing the VFO frequency) are performed.
+// The implementation of DoTheMIDI is tightly bound to pihpsr.
+//
+
+void DoTheMidi(enum MIDIaction code, enum MIDItype type, int val);
diff --git a/midi.inp b/midi.inp
new file mode 100644
index 0000000..88076ce
--- /dev/null
+++ b/midi.inp
@@ -0,0 +1,33 @@
+#
+# Sample midi.inp file, suitable for a Behringer CMD PL-1 MIDI controller
+#
+# Note that the Attenuator is implemented twice, as a key and as a wheel
+# The key is suitable for radios with a step (ALEX) attenuator, the wheel
+# fits best for radios with a programmable attenuator (0-31 dB)
+#
+DEVICE=CMD PL 1
+CTRL=31 WHEEL THR=59 61 63 65 67 69 ACTION=VFO           # Big wheel:    : main VFO knob
+PITCH ACTION=AFGAIN                                      # Big slider    : AF gain
+KEY=16 ACTION=PREAMP					 # Key 1         : Cycle through Preamp settings
+KEY=17 ACTION=ATT 					 # Key 2         : Cycle through ATT (Alex ATT) settings
+KEY=18 ACTION=RITTOGGLE					 # Key 3         : RIT on/off
+KEY=19 ACTION=NONE					 # Key 4         :
+KEY=20 ACTION=NONE					 # Key 5         :
+KEY=21 ACTION=NONE					 # Key 6         :
+KEY=22 ACTION=NONE					 # Key 7         :
+KEY=23 ACTION=NONE					 # Key 8         :
+KEY=24 ACTION=TUNE                                       # LOAD    button: TUNE on/off
+KEY=27 ACTION=SWAPVFO 					 # SCRATCH button: Swap VFOs A and B
+KEY=34 ACTION=MOX                                        # CUE     button: MOX on/off
+KEY=36 ACTION=MODEDOWN					 # <<      button: Mode down
+KEY=37 ACTION=MODEUP					 # >>      button: Mode up
+KEY=38 ACTION=BANDDOWN					 # -       button: Band down
+KEY=39 ACTION=BANDUP					 # +       button: Band up
+CTRL=0 WHEEL THR=-1 -1 63 65 128 128 ACTION=ATT          # Knob 1        : RX att
+CTRL=1 WHEEL THR=-1 -1 63 65 128 128 ACTION=COMPRESS     # Knob 2        : TX compression
+CTRL=2 WHEEL THR=-1 -1 63 65 128 128 ACTION=RITVAL       # Knob 3        : RIT value
+CTRL=3 WHEEL THR=-1 -1 63 65 128 128 ACTION=PANLOW       # Knob 4        : Panadapter low
+CTRL=4 WHEEL THR=-1 -1 63 65 128 128 ACTION=AGC          # Knob 5        : AGC
+CTRL=5 WHEEL THR=-1 -1 63 65 128 128 ACTION=MICGAIN      # Knob 6        : MIC gain
+CTRL=6 WHEEL THR=-1 -1 63 65 128 128 ACTION=RFPOWER      # Knob 7        : TX drive
+CTRL=7 WHEEL THR=-1 -1 63 65 128 128 ACTION=FILTERUP     # Knob 8        : cycle through the filters
diff --git a/midi2.c b/midi2.c
new file mode 100644
index 0000000..1e737c1
--- /dev/null
+++ b/midi2.c
@@ -0,0 +1,257 @@
+/*
+ * Layer-2 of MIDI support
+ *
+ * (C) Christoph van Wullen, DL1YCF
+ *
+ * Using the data in MIDICommandsTable, this subroutine translates the low-level
+ * MIDI events into MIDI actions in the SDR console.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include "midi.h"
+
+void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
+
+    struct desc *desc;
+    int new;
+    static enum MIDIaction last_wheel_action;
+    static struct timespec tp, last_wheel_tp={0,0};
+    long delta;
+
+    if (event == MIDI_PITCH) {
+	desc=MidiCommandsTable.pitch;
+    } else {
+	desc=MidiCommandsTable.desc[note];
+    }
+    while (desc) {
+	if ((desc->channel == channel || desc->channel == -1) && (desc->event == event)) {
+	    // Found matching entry
+	    switch (desc->event) {
+		case MIDI_NOTE:
+		    if ((val == 1 || (val == 0 && desc->onoff == 1)) && desc->type == MIDI_KEY) {
+			DoTheMidi(desc->action, desc->type, 0);
+		    }
+		    break;
+		case MIDI_CTRL:
+		    if (desc->type == MIDI_KNOB) {
+			// normalize value to range 0 - 100
+			new = (val*100)/127;
+			DoTheMidi(desc->action, desc->type, new);
+		    } else if (desc->type == MIDI_WHEEL) {
+			if (desc->delay > 0) {
+			  clock_gettime(CLOCK_MONOTONIC, &tp);
+			  delta=1000*(tp.tv_sec - last_wheel_tp.tv_sec);
+			  delta += (tp.tv_nsec - last_wheel_tp.tv_nsec)/1000000;
+			  if (delta < desc->delay) break;
+			  last_wheel_tp = tp;
+			}
+			// translate value to direction
+			if (val <= desc->low_thr1) new=-1;
+			if (val <= desc->low_thr2) new=-10;
+			if (val <= desc->low_thr3) new=-100;
+			if (val >= desc->up_thr1 ) new=1;
+			if (val >= desc->up_thr2 ) new=10;
+			if (val >= desc->up_thr3 ) new=100;
+			DoTheMidi(desc->action, desc->type, new);
+			last_wheel_action=desc->action;
+		    }
+		    break;
+		case MIDI_PITCH:
+		    if (desc->type == MIDI_KNOB) {
+			// normalize value to 0 - 100
+			new = (val*100)/16383;
+			fprintf(stderr,"PITCH calc: val=%d new=%d\n", val,new);
+			DoTheMidi(desc->action, desc->type, new);
+		    }
+		    break;
+		default:
+		    break;
+	    }
+	    break;
+	} else {
+	    desc=desc->next;
+	}
+    }
+}
+
+/*
+ * This data structre connects names as used in the midi.inp file with
+ * our MIDIaction enum values
+ */
+
+static struct {
+  enum MIDIaction action;
+  const char *str;
+} ActionTable[] = {
+        { VFO,          "VFO"},
+        { TUNE,         "TUNE"},
+        { MOX,          "MOX"},
+        { AF_GAIN,      "AFGAIN"},
+        { MIC_VOLUME,   "MICGAIN"},
+        { TX_DRIVE,     "RFPOWER"},
+        { ATT,          "ATT"},
+        { PRE,          "PREAMP"},
+        { AGC,          "AGC"},
+        { COMPRESS,     "COMPRESS"},
+        { RIT_ONOFF,    "RITTOGGLE"},
+        { RIT_VAL,      "RITVAL"},
+        { PAN_HIGH,     "PANHIGH"},
+        { PAN_LOW,      "PANLOW"},
+        { BAND_UP,      "BANDUP"},
+        { BAND_DOWN,    "BANDDOWN"},
+        { FILTER_UP,    "FILTERUP"},
+        { FILTER_DOWN,  "FILTERDOWN"},
+	{ MODE_UP,	"MODEUP"},
+	{ MODE_DOWN,	"MODEDOWN"},
+	{ SWAP_VFO,	"SWAPVFO"},
+        { ACTION_NONE,  NULL}
+};
+
+/*
+ * Translation from keyword in midi.inp file to MIDIaction
+ */
+
+static enum MIDIaction keyword2action(char *s) {
+    int i=0;
+
+    for (i=0; 1; i++) {
+	if (ActionTable[i].str == NULL) return ACTION_NONE;
+	if (!strcmp(s, ActionTable[i].str)) return ActionTable[i].action;
+   }
+   /* NOTREACHED */
+}
+
+/*
+ * Here we read in a MIDI description file "midi.def" and fill the MidiCommandsTable
+ * data structure
+ */
+
+void MIDIstartup() {
+    FILE *fpin,*fpout;
+    char zeile[255];
+    char *cp,*cq;
+    int key;
+    enum MIDIaction action;
+    int chan;
+    int is_wheel;
+    int lt3,lt2,lt1,ut1,ut2,ut3;
+    int onoff, delay;
+    struct desc *desc,*dp;
+    enum MIDItype type;
+    enum MIDIevent event;
+    int i;
+
+    for (i=0; i<128; i++) MidiCommandsTable.desc[i]=NULL;
+    MidiCommandsTable.pitch=NULL;
+
+    fpin=fopen("midi.inp", "r");
+    fpout=stderr;
+    if (!fpin) return;
+
+    for (;;) {
+      if (fgets(zeile, 255, fpin) == NULL) break;
+
+      // ignore comments
+      cp=index(zeile,'#');
+      if (cp == zeile) continue;   // comment line
+      if (cp) *cp=0;               // ignore trailing comment
+
+      if ((cp = strstr(zeile, "DEVICE="))) {
+        // Delete trailing blanks and newlines
+	cq=cp+7;
+        while (*cq != 0 && *cq != '\n' && *cq != ' ' && *cq != '\t') cq++;
+	*cq=0;
+	register_midi_device(cp+7);
+        continue; // nothing more in this line
+      }
+      chan=-1;  // default: any channel
+      lt3=lt2=lt1=0;
+      ut3=ut2=ut1=127;
+      onoff=0;
+      event=EVENT_NONE;
+      type=TYPE_NONE;
+      key=0;
+      delay=0;
+
+      if ((cp = strstr(zeile, "KEY="))) {
+        sscanf(cp+4, "%d", &key);
+        event=MIDI_NOTE;
+	type=MIDI_KEY;
+      }
+      if ((cp = strstr(zeile, "CTRL="))) {
+        sscanf(cp+5, "%d", &key);
+	event=MIDI_CTRL;
+	type=MIDI_KNOB;
+      }
+      if ((cp = strstr(zeile, "PITCH "))) {
+        event=MIDI_PITCH;
+	type=MIDI_KNOB;
+      }
+      if ((cp = strstr(zeile, "CHAN="))) {
+        sscanf(cp+5, "%d", &chan);
+	chan--;
+        if (chan<0 || chan>15) chan=-1;
+      }
+      if ((cp = strstr(zeile, "WHEEL"))) {
+	// change type from MIDI_KNOB to MIDI_WHEEL
+        type=MIDI_WHEEL;
+      }
+      if ((cp = strstr(zeile, "ONOFF"))) {
+        onoff=1;
+      }
+      if ((cp = strstr(zeile, "DELAY="))) {
+        sscanf(cp+6, "%d", &delay);
+      }
+      if ((cp = strstr(zeile, "THR="))) {
+        sscanf(cp+4, "%d %d %d %d %d %d", &lt3, &lt2, &lt1, &ut1, &ut2, &ut3);
+        is_wheel=1;
+      }
+      if ((cp = strstr(zeile, "ACTION="))) {
+        // cut zeile at the first blank character following
+        cq=cp+7;
+        while (*cq != 0 && *cq != '\n' && *cq != ' ' && *cq != '\t') cq++;
+	*cq=0;
+        action=keyword2action(cp+7);
+      }
+      if (event == EVENT_NONE || type == TYPE_NONE || key < 0 || key > 127) continue;
+      // Now all entries of the line have been read. Construct descriptor
+      fprintf(fpout,"K=%d C=%d T=%d E=%d A=%d OnOff=%d THRs=%d %d %d %d %d %d\n",
+	 key,chan,type, event, action, onoff, lt3,lt2,lt1,ut1,ut2,ut3);
+      desc = (struct desc *) malloc(sizeof(struct desc));
+      desc->next = NULL;
+      desc->action = action;
+      desc->type = type;
+      desc->event = event;
+      desc->onoff = onoff;
+      desc->delay = delay;
+      desc->low_thr3 = lt3;
+      desc->low_thr2 = lt2;
+      desc->low_thr1 = lt1;
+      desc->up_thr1  = ut1;
+      desc->up_thr2  = ut2;
+      desc->up_thr3  = ut3;
+      desc->channel  = chan;
+      // insert descriptor
+      if (event == MIDI_PITCH) {
+	dp = MidiCommandsTable.pitch;
+	if (dp == NULL) {
+	  MidiCommandsTable.pitch = desc;
+	} else {
+	  while (dp->next != NULL) dp=dp->next;
+	  dp->next=desc;
+	}
+      }
+      if (event == MIDI_KEY || event == MIDI_CTRL) {
+	dp = MidiCommandsTable.desc[key];
+	if (dp == NULL) {
+	  MidiCommandsTable.desc[key]=desc;
+	} else {
+	  while (dp->next != NULL) dp=dp->next;
+	  dp->next=desc;
+	}
+      }
+    }
+}
diff --git a/midi3.c b/midi3.c
new file mode 100644
index 0000000..f3989d0
--- /dev/null
+++ b/midi3.c
@@ -0,0 +1,233 @@
+/*
+ * Layer-3 of MIDI support
+ * 
+ * (C) Christoph van Wullen, DL1YCF
+ *
+ *
+ * In most cases, a certain action only makes sense for a specific
+ * type. For example, changing the VFO frequency will only be implemeted
+ * for MIDI_WHEEL, and TUNE off/on only with MIDI_KNOB.
+ *
+ * However, changing the volume makes sense both with MIDI_KNOB and MIDI_WHEEL.
+ */
+#include "radio.h"
+#include "vfo.h"
+#include "filter.h"
+#include "band.h"
+#include "mode.h"
+#include "new_menu.h"
+#include "sliders.h"
+#include "ext.h"
+#include "midi.h"
+
+void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
+
+    int new;
+    double dnew;
+    double *dp;
+
+    switch (action) {
+	case SWAP_VFO:	// only key supported
+	    if (type == MIDI_KEY) {
+		g_idle_add(ext_vfo_a_swap_b,NULL);
+	    }
+	    break;    
+	case VFO: // only wheel supported
+	    if (type == MIDI_WHEEL) g_idle_add(ext_vfo_step, (gpointer)(uintptr_t) val);
+	    break;
+	case TUNE: // only key supported
+	    if (type == MIDI_KEY) {
+	        new = !tune;
+		g_idle_add(ext_tune_update, (gpointer)(long) new);
+	    }
+	    break;    
+	case MOX: // only key supported
+	    if (type == MIDI_KEY) {
+	        new = !mox;
+		g_idle_add(ext_mox_update, (gpointer)(long) new);
+	    }
+	    break;    
+	case AF_GAIN: // knob or wheel supported
+	    if (type == MIDI_KNOB) {
+		active_receiver->volume = 0.01*val;
+	    } else  if (type == MIDI_WHEEL) {
+		dnew=active_receiver->volume += 0.01*val;
+		if (dnew < 0.0) dnew=0.0; if (dnew > 1.0) dnew=1.0;
+		active_receiver->volume = dnew;
+	    } else {
+		break;
+	    }
+	    g_idle_add(ext_update_af_gain, NULL);
+	    break;
+	case MIC_VOLUME: // knob or wheel supported
+	    if (type == MIDI_KNOB) {
+		dnew=-10.0 + 0.6*val;
+	    } else if (type == MIDI_WHEEL) {
+		dnew = mic_gain + val;
+		if (dnew < -10.0) dnew=-10.0; if (dnew > 50.0) dnew=50.0;
+	    } else {
+		break;
+	    }
+	    dp=malloc(sizeof(double));
+	    *dp=dnew;
+	    g_idle_add(ext_set_mic_gain, (gpointer) dp);
+	    break;
+	case AGC: // knob or wheel supported
+	    if (type == MIDI_KNOB) {
+		dnew = -20.0 + 1.4*val;
+	    } else if (type == MIDI_WHEEL) {
+		dnew=active_receiver->agc_gain + val;
+		if (dnew < -20.0) dnew=-20.0; if (dnew > 120.0) dnew=120.0;
+	    } else {
+		break;
+	    }
+	    dp=malloc(sizeof(double));
+	    *dp=dnew;
+	    g_idle_add(ext_set_agc_gain, (gpointer) dp);
+	    break;
+	case TX_DRIVE: // knob or wheel supported
+	    if (type == MIDI_KNOB) {
+		dnew = val;
+	    } else if (type == MIDI_WHEEL) {
+		dnew=transmitter->drive + val;
+		if (dnew < 0.0) dnew=0.0; if (dnew > 100.0) dnew=100.0;
+	    } else {
+		break;
+	    }
+	    dp=malloc(sizeof(double));
+	    *dp=dnew;
+	    g_idle_add(ext_set_drive, (gpointer) dp);
+	    break;
+	case BAND_UP:     // key or wheel supported
+	case BAND_DOWN:   // key or wheel supported
+	    if (type == MIDI_KEY) {
+		new=(action == BAND_UP) ? 1 : -1;
+	    } else if (type == MIDI_WHEEL) {
+		new=val;
+	    } else {
+		break;
+	    }
+            new+=vfo[active_receiver->id].band;
+            if (new >= BANDS) new=0;
+            if (new < 0) new=BANDS-1;
+	    g_idle_add(ext_vfo_band_changed, (gpointer) (uintptr_t) new);
+	    break;
+	case FILTER_UP:      // key or wheel supported
+	case FILTER_DOWN:    // key or wheel supported
+	    if (type == MIDI_KEY) {
+		new=(action == FILTER_UP) ? 1 : -1;
+	    } else if (type == MIDI_WHEEL) {
+		new=val;
+	    } else {
+		break;
+	    }
+	    new+=vfo[active_receiver->id].filter;
+	    if (new >= FILTERS) new=0;
+	    if (new <0) new=FILTERS-1;
+	    g_idle_add(ext_vfo_filter_changed, (gpointer) (uintptr_t) new);
+	    break;
+	case MODE_UP:      // key or wheel supported
+	case MODE_DOWN:    // key or wheel supported
+	    if (type == MIDI_KEY) {
+		new=(action == MODE_UP) ? 1 : -1;
+	    } else if (type == MIDI_WHEEL) {
+		new=val;
+	    } else {
+		break;
+	    }
+	    new+=vfo[active_receiver->id].mode;
+	    if (new >= MODES) new=0;
+	    if (new <0) new=MODES-1;
+	    g_idle_add(ext_vfo_mode_changed, (gpointer) (uintptr_t) new);
+	    break;
+	case PAN_LOW:  // only wheel supported
+	    if (type == MIDI_WHEEL) {
+		if (isTransmitting()) {
+		    // TX panadapter affected
+		    transmitter->panadapter_low += val;
+		} else {
+		    active_receiver->panadapter_low += val;
+		}
+	    }
+	    break;
+	case RIT_ONOFF:  // only key supported
+	    if (type == MIDI_KEY) {
+		vfo[active_receiver->id].rit_enabled = !vfo[active_receiver->id].rit_enabled;
+	        g_idle_add(ext_vfo_update, NULL);
+	    }
+	    break;
+	case RIT_VAL:	// only wheel supported
+	    if (type == MIDI_WHEEL) {
+		new = vfo[active_receiver->id].rit + val*rit_increment;
+		if (new >  9999) new= 9999;
+		if (new < -9999) new=-9999;
+		vfo[active_receiver->id].rit = new;
+	        g_idle_add(ext_vfo_update, NULL);
+	    }
+	    break;
+	case PAN_HIGH:  // only wheel supported
+	    if (type == MIDI_WHEEL) {
+		if (mox) {
+		    // TX panadapter affected
+		    transmitter->panadapter_high += val;
+		} else {
+		    active_receiver->panadapter_high += val;
+		}
+	    }
+	    break;
+	case PRE:	// only key supported
+	    if (filter_board == CHARLY25) {
+		new = active_receiver->preamp + active_receiver->dither;
+		new++;
+		if (new >2) new=0;
+		switch (new) {
+		    case 0:
+			active_receiver->preamp=0;
+			active_receiver->dither=0;
+			break;
+		    case 1:
+			active_receiver->preamp=1;
+			active_receiver->dither=0;
+			break;
+		    case 2:
+			active_receiver->preamp=1;
+			active_receiver->dither=1;
+			break;
+		}
+		g_idle_add(ext_update_att_preamp, NULL);
+	    } else {
+		new=active_receiver->preamp+1;
+		if (new > 1) new=0;
+		active_receiver->preamp= (new == 1);
+	    }
+	    break;
+	case ATT:	// Key for ALEX attenuator, wheel or knob for slider
+	    switch(type) {
+		case MIDI_KEY:
+		    new=active_receiver->alex_attenuation + 1;
+		    if (new > 3) new=0;
+		    g_idle_add(ext_set_alex_attenuation, (gpointer)(uintptr_t)new);
+		    g_idle_add(ext_update_att_preamp, NULL);
+		    break;
+		case MIDI_WHEEL:
+		case MIDI_KNOB:
+		    if (type == MIDI_WHEEL) {
+		      new=adc_attenuation[active_receiver->adc] + val;
+		      if (new > 31) new=31;
+		      if (new < 0 ) new=0;
+		    } else {
+		      new=(31*val)/100;
+		    }
+		    dp=malloc(sizeof(double));
+		    *dp=new;
+		    g_idle_add(ext_set_attenuation_value,(gpointer) dp);
+		    break;
+		default:
+		    break;
+	    }
+	    break;
+	default:
+            fprintf(stderr,"Unimplemented in DoTheMidi: A=%d T=%d val=%d\n", action, type, val);
+	    break;
+    }
+}
diff --git a/old_discovery.c b/old_discovery.c
index 024f99e..235ca12 100644
--- a/old_discovery.c
+++ b/old_discovery.c
@@ -31,6 +31,7 @@
 #include <ifaddrs.h>
 #include <string.h>
 #include <errno.h>
+#include <fcntl.h>
 
 #include "discovered.h"
 #include "discovery.h"
@@ -81,9 +82,12 @@ static void discover(struct ifaddrs* iface) {
 	//
 	// We make a time-out of 3 secs, otherwise we might "hang" in connect()
 	//
+	flags=fcntl(discovery_socket, F_GETFL, 0);
+	fcntl(discovery_socket, F_SETFL, flags | O_NONBLOCK);
         tv.tv_sec=3;
 	tv.tv_usec=0;
 	setsockopt(discovery_socket, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+	
 
         if (connect(discovery_socket, (const struct sockaddr *)&to_addr, sizeof(to_addr)) < 0) {
             perror("discover: connect() failed for TCP discovery_socket:");
diff --git a/ps_menu.c b/ps_menu.c
index 37a5c60..d6d5924 100644
--- a/ps_menu.c
+++ b/ps_menu.c
@@ -221,7 +221,7 @@ static int info_thread(gpointer arg) {
 
     if (transmitter->auto_on) {
       double ddb;
-      int new_att;
+      static int new_att;
       int newcal=info[5]!=old5_2;
       old5_2=info[5];
       switch(state) {
@@ -244,6 +244,7 @@ static int info_thread(gpointer arg) {
 	    // Actually, we first adjust the attenuation (state=0),
 	    // then do a PS reset (state=1), and then restart PS (state=2).
             if (transmitter->attenuation != new_att) {
+              SetPSControl(transmitter->id, 1, 0, 0, 0);
 	      transmitter->attenuation=new_att;
               state=1;
 	    }
@@ -268,11 +269,10 @@ static void enable_cb(GtkWidget *widget, gpointer data) {
 
 static void auto_cb(GtkWidget *widget, gpointer data) {
   transmitter->auto_on=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
-  if(transmitter->auto_on) {
-    transmitter->attenuation=31;
-  } else {
+  if(!transmitter->auto_on) {
     transmitter->attenuation=0;
   }
+
 }
 
 static void resume_cb(GtkWidget *widget, gpointer data) {
diff --git a/radio.c b/radio.c
index c9bb32c..b47623e 100644
--- a/radio.c
+++ b/radio.c
@@ -69,6 +69,9 @@
 #ifdef LOCALCW
 #include "iambic.h"
 #endif
+#ifdef MIDI
+#include "midi.h"
+#endif
 
 #define min(x,y) (x<y?x:y)
 #define max(x,y) (x<y?y:x)
@@ -361,6 +364,11 @@ void start_radio() {
   gdk_window_set_cursor(gtk_widget_get_window(top_window),gdk_cursor_new(GDK_WATCH));
 
   int rc;
+
+#ifdef MIDI
+  MIDIstartup();
+#endif
+
 #ifdef __APPLE__
   property_sem=sem_open("PROPERTY", O_CREAT, 0700, 0);
   rc=(property_sem == SEM_FAILED);
diff --git a/rigctl.c b/rigctl.c
index 442611a..f60f5db 100644
--- a/rigctl.c
+++ b/rigctl.c
@@ -2445,10 +2445,9 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
 						       } else {
 							  int tval = atoi(&cmd_input[2]);                
 							  new_vol = (double) (tval * 60/100) - 10; 
-							  //set_mic_gain(new_vol); 
 							  double *p_mic_gain=malloc(sizeof(double));
 							  *p_mic_gain=new_vol;
-							  g_idle_add(update_mic_gain,(void *)p_mic_gain);
+							  g_idle_add(ext_set_mic_gain,(void *)p_mic_gain);
 						       }
 						    } else {
 						       if(len <=2) {
@@ -2459,7 +2458,7 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
                                                           if((new_vol >= -10) && (new_vol <= 50)) {
 							     double *p_mic_gain=malloc(sizeof(double));
 							     *p_mic_gain=new_vol;
-							     g_idle_add(update_mic_gain,(void *)p_mic_gain);
+							     g_idle_add(ext_set_mic_gain,(void *)p_mic_gain);
                                                           } else {
                                                              send_resp(client_sock,"?;");
                                                           }
diff --git a/sliders.c b/sliders.c
index c31afef..8c71cae 100644
--- a/sliders.c
+++ b/sliders.c
@@ -188,12 +188,22 @@ void set_attenuation_value(double value) {
 
 void update_att_preamp(void) {
   // CHARLY25: update the ATT/Pre buttons to the values of the active RX
+  // We should also set the attenuation for use in meter.c
   if (filter_board == CHARLY25) {
     char id[] = "x";
+    if (active_receiver->id != 0) {
+      active_receiver->alex_attenuation=0;
+      active_receiver->preamp=0;
+      active_receiver->dither=0;
+      adc_attenuation[active_receiver->adc] = 0;
+    }
     sprintf(id, "%d", active_receiver->alex_attenuation);
+    adc_attenuation[active_receiver->adc] = 12*active_receiver->alex_attenuation;
     gtk_combo_box_set_active_id(GTK_COMBO_BOX(c25_att_combobox), id);
     sprintf(id, "%d", active_receiver->preamp + active_receiver->dither);
     gtk_combo_box_set_active_id(GTK_COMBO_BOX(c25_preamp_combobox), id);
+  } else {
+    adc_attenuation[active_receiver->adc] = 10*active_receiver->alex_attenuation;
   }
 }
 
@@ -385,12 +395,6 @@ void set_mic_gain(double value) {
   }
 }
 
-int update_mic_gain(void *data) {
-  set_mic_gain(*(double*)data);
-  free(data);
-  return 0;
-}
-
 void set_linein_gain(int value) {
   linein_gain=value;
   if(display_sliders) {
@@ -489,7 +493,7 @@ static void compressor_enable_cb(GtkWidget *widget, gpointer data) {
   transmitter_set_compressor(transmitter,gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
 }
 
-void set_squelch(RECEIVER* rx) {
+void set_squelch() {
   setSquelch(active_receiver);
   if(display_sliders) {
     gtk_range_set_value (GTK_RANGE(squelch_scale),active_receiver->squelch);
diff --git a/sliders.h b/sliders.h
index 87904f7..a33ac7f 100644
--- a/sliders.h
+++ b/sliders.h
@@ -43,7 +43,7 @@ extern GtkWidget *sliders_init(int my_width, int my_height);
 
 extern void sliders_update();
 
-extern void set_squelch(RECEIVER* rx);
+extern void set_squelch();
 extern void set_compression(TRANSMITTER *tx);
 
 #endif
-- 
2.45.2