From: c vw <dl1ycf@darc.de>
Date: Thu, 30 May 2019 09:59:08 +0000 (+0200)
Subject: a) fixed seg-faults associated with RX2 panel
X-Git-Url: https://git.rkrishnan.org/pf/content/frontends//%22?a=commitdiff_plain;h=34bb771ca43c3a3ba9e025bb5b873e457a2ba581;p=pihpsdr.git

a) fixed seg-faults associated with RX2 panel
b) finalized MIDI
c) reworked PURESIGNAL ADC associations
---

diff --git a/README.MIDI b/README.MIDI
index 4f248df..57a31f9 100644
--- a/README.MIDI
+++ b/README.MIDI
@@ -136,33 +136,44 @@ drive could be used with a knob (CTRL but no WHEEL), with a wheel
 
 Here is a list of actions currently implemented, in alphabetical
 order. We also give for which types of MIDI events (KEY, KNOB,
-WHEEL, PITCH) this action is defined
+WHEEL) this action is defined. We further have a "dummy" action
+NONE which means that there is nothing to do.
 
 
 Action         Event                Explanation
 ----------------------------------------------------------------------------------------------
 AFGAIN         KNOB,WHEEL           AF audio volume
 AGC            KNOB,WHEEL           AGC level
+AGCATTACK      KEY                  cycle through AGC settings (fast/slow, etc.)
 ATT            KEY                  cycle  through "ALEX attenuator" settings
 ATT            KNOB,WHEEL           set RX attenuation (0-31dB)
 BANDUP         KEY,WHEEL            cycle through the bands (upwards)
 BANDDOWN       KEY,WHEEL            cycle through the bands (downwards)
-COMPRESS       KNOB,WHEEL           set TX compression level (if compression is activated)
+COMPRESS       KNOB,WHEEL           set TX compression level
+CTUN           KEY                  toggle CTUN mode
 FILTERUP       KEY,WHEEL            cycle through the filters (upwards)
 FILTERDOWN     KEY,WHEEL            cycle through the filster (downwards)
+LOCK           KEY                  lock VFO(s)
 MICGAIN        KNOB,WHEEL           MIC gain
 MODEUP         KEY,WHEEL            cycle through the modes (upwards)
 MODEDOWN       KEY,WHEEL            cycle through the modes (downwards)
 MOX            KEY                  MOX on/off
+NB             KEY                  cycle through NoiseBlanker settings
+NR             KEY                  cycle through NoiseReduction settings
 PANHIGH        WHEEL                change "high" level of current pan-adapter
 PANLOW         WHEEL                change "bottom" level of current pan-adapter
 PREAMP         KEY                  cycle through preamp settings (only available for CHARLY25 filter boards)
-RITTOGGLE      KEY                  RIT on/off
+PURESIGNAL     KEY                  toggle PURESIGNAL (on/off)
+RITCLEAR       KEY                  clear RIT frequency
 RITVAL         WHEEL                change RIT frequency offset
 RFPOWER        KNOB,WHEEL           TX power
+SPLIT          KEY                  toggle Split mode
 SWAPVFO        KEY                  Swap VFO-A/VFO-B
 TUNE           KEY                  Tune on/off
 VFO            WHEEL                change VFO frequency
+VFOA2B         KEY                  frequency VFO A -> VFO B
+VFOB2A         KEY                  frequency VFO B -> VFO A
+VOX            KEY                  toggle VOX (on/off)
  
 
 If you need more or different type of actions, the implementation should be straightforward.
diff --git a/audio.c b/audio.c
index cf49fee..4c98c8d 100644
--- a/audio.c
+++ b/audio.c
@@ -18,8 +18,8 @@
 */
 
 //
-// DL1YCF: If PortAudio is used instead of ALSO (e.g. on MacOS),
-//         this file is not used (and replaced by portaudio.c).
+// If PortAudio is used instead of ALSA (e.g. on MacOS),
+// this file is not used (and replaced by portaudio.c).
 
 #ifndef PORTAUDIO 
 
diff --git a/ext.c b/ext.c
index c86cd5c..daacc6d 100644
--- a/ext.c
+++ b/ext.c
@@ -179,6 +179,16 @@ int ext_vfo_a_swap_b(void *data) {
   return 0;
 }
 
+int ext_vfo_a_to_b(void *data) {
+  vfo_a_to_b();
+  return 0;
+}
+
+int ext_vfo_b_to_a(void *data) {
+  vfo_b_to_a();
+  return 0;
+}
+
 int ext_update_att_preamp(void *data) {
   update_att_preamp();
   return 0;
diff --git a/ext.h b/ext.h
index 070a215..c3c9710 100644
--- a/ext.h
+++ b/ext.h
@@ -63,6 +63,8 @@ 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_vfo_a_to_b(void *data);
+int ext_vfo_b_to_a(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/frequency.c b/frequency.c
index 8c9eca2..d69fe76 100644
--- a/frequency.c
+++ b/frequency.c
@@ -436,7 +436,7 @@ char* getFrequencyInfo(long long frequency,int filter_low,int filter_high) {
             break;
         } else if ((info->maxFrequency +1) == ((info+1)->minFrequency) &&
                    flow>=info->minFrequency && fhigh <= (info+1)->maxFrequency) {
-            // DL1YCF:
+            //
 	    // Sometimes in DigiMode operation, the nominal USB TX channel crosses
 	    // a sub-band boundary. Example: FT8 on 17m, VFO set to 18099 kHz because
 	    // FT8 signal is generated at 18100.8 kHz with an AF signal of 1800 Hz.
@@ -445,6 +445,7 @@ char* getFrequencyInfo(long long frequency,int filter_low,int filter_high) {
 	    // either sub-band. We can return the info for the lower sub-band if
 	    // the band and transmit data is the same in both cases. If they are
 	    // different, we have found no match so far.
+	    //
 	    if ((info->band == (info+1)->band) && (info->transmit == (info+1)->transmit)) {
 	  	result = info->info;
 		break;
diff --git a/hpsdrsim.c b/hpsdrsim.c
index 6ecf73c..c267d14 100644
--- a/hpsdrsim.c
+++ b/hpsdrsim.c
@@ -13,24 +13,33 @@
  *
  * RF1: ADC noise (16-bit ADC) plus a  800 Hz signal at -100dBm
  * RF2: ADC noise (16-bit ADC) plus a 2000 Hz signal at - 80dBm
- * RF3: TX feedback signal with some distortion. Signal strength
- *      according to the "TX drive" and "TX ATT" settings.
- * RF4: normalized undistorted TX signal with a peak value of 0.400
+ * RF3: TX feedback signal with some distortion.
+ * RF4: normalized undistorted TX signal with a peak value of 0.407
  *
- * RF1 and RF2 respect the Preamp and Attenuator settings
+ * RF1 and RF2 signal strenght vary according to Preamp and Attenuator settings
+ * RF3 signal strength varies according to TX-drive and TX-ATT settings
+ * RF4 signal strength is normalized to amplitude of 0.407
  *
- * 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, 
+ * RF1 and RF2 are associated with ADC1 and ADC2.
+ * RF3 also goes to ADC1 upon TX
+ * RF4 is the TX DAC signal. Upon TX, it goes to RX2 for Metis, RX4 for Hermes, and RX5 beyond.
  *
- * The connection upon transmitting depends on the DEVICE, namely
+ * The SDR application has to make the proper ADC settings!
  *
- * 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)
+ * Without PURESIGNAL, the standard association is:
+ * RX1=ADC1, RX2=ADC2  (ADC2 not present for METIS and ANAN-10/10E/100)
  *
+ * When using PURESIGNAL, the standard association is:
+ *
+ * DEVICE=METIS:  RX1=ADC1, RX2=TX-DAC
+ * DEVICE=HERMES: RX1=ADC1, RX2=ADC1, RX3=ADC2, RX4=TX-DAC
+ * DEVICE=ORION:  RX1=ADC1, RX2=ADC1, RX3=ADC3, RX4=ADC1, RX5=TX-DAC (also for ANGELIA and ORION2)
+ *
+ * Note that for STEMlab (RedPitaya) based SDRs, there is usually a fixed association
+ *
+ * RX1=ADC1, RX2=ADC2, RX3=ADC2, RX4=TX-DAC
+ *
+ * And this setting is NOT AFFECTED by the ADC settings in the HPSDR protocol.
  *
  * Audio sent to the "radio" is played via the first available output channel.
  * This works on MacOS (PORTAUDIO) and Linux (ALSA).
@@ -131,7 +140,7 @@ static int		lna6m=-1;
 static int		alexTRdisable=-1;
 static int		vna=-1;
 static int		c25_ext_board_i2c_data=-1;
-static int		rx_adc[7]={0,1,1,2,-1,-1,-1};
+static int		rx_adc[7]={-1,-1,-1,-1,-1,-1,-1};
 static int		cw_hang = -1;
 static int		cw_reversed = -1;
 static int		cw_speed = -1;
@@ -262,7 +271,7 @@ int main(int argc, char *argv[])
 	    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;
+	ismetis=ishermes=isorion=isc25=0;
 	switch (DEVICE) {
 	    case   0: fprintf(stderr,"DEVICE is METIS\n");   ismetis=1;  break;
 	    case   1: fprintf(stderr,"DEVICE is HERMES\n");  ishermes=1; break;
@@ -855,6 +864,12 @@ void process_ep2(uint8_t *frame)
             chk_data((frame[2] & 0x30) >> 4, rx_adc[6], "RX7 ADC");
 	    chk_data((frame[3] & 0x1f), txatt, "TX ATT");
 	    txatt_dbl=pow(10.0, -0.05*(double) txatt);
+	    if (isc25) {
+		// RedPitaya: Hard-wired ADC settings.
+		rx_adc[0]=0;
+		rx_adc[1]=1;
+		rx_adc[2]=1;
+	    }
 	    break;
 
 	case 30:
@@ -1009,81 +1024,61 @@ 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];
-			//
+			// ADC1: noise + weak tone on RX, feedback sig. on TX
+		        if (ptt) {
+			  i1=isample[rxptr]*txdrv_dbl;
+			  q1=qsample[rxptr]*txdrv_dbl;
+			  fac=IM3a+IM3b*(i1*i1+q1*q1);
+			  rf1isample= (txatt_dbl*i1*fac+noiseItab[noiseIQpt]) * 8388607.0;
+			  rf1qsample= (txatt_dbl*q1*fac+noiseItab[noiseIQpt]) * 8388607.0;
+			} else {
+			  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, with some ADC noise
-			//
-			i1=isample[rxptr]*txdrv_dbl;
-			q1=qsample[rxptr]*txdrv_dbl;
-			fac=IM3a+IM3b*(i1*i1+q1*q1);
-			rf3isample= (txatt_dbl*i1*fac+noiseItab[noiseIQpt]) * 8388607.0;
-			rf3qsample= (txatt_dbl*q1*fac+noiseItab[noiseIQpt]) * 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
-				if (ptt && ismetis) {
-				    myisample=rf3isample;
-				    myqsample=rf3qsample;
-				} else {
-				    myisample=rf1isample;
-				    myqsample=rf1qsample;
-				}
-				break;
-			      case 1: // RX2
-				if (ptt && ismetis) {
-				    myisample=rf4isample;
-				    myqsample=rf4qsample;
-				} else {
-				    myisample=rf2isample;
-				    myqsample=rf2qsample;
-				}
+			    switch (rx_adc[k]) {
+			      case 0: // ADC1
+				myisample=rf1isample;
+				myqsample=rf1qsample;
 				break;
-			      case 2:
-                                if (ptt && ishermes) {
-				    myisample=rf3isample;
-				    myqsample=rf3qsample;
-                                } else {
-				    myisample=rf2isample;
-				    myqsample=rf2qsample;
-				}
+			      case 1: // ADC2
+				myisample=rf2isample;
+				myqsample=rf2qsample;
 				break;
-			      case 3: // RX4
-                        	if (ptt && ishermes) {
-				    myisample=rf4isample;
-				    myqsample=rf4qsample;
-                               	} else if (ptt && isorion) {
-				    myisample=rf3isample;
-				    myqsample=rf3qsample;
-				}
-				break;
-			      case 4: // RX5
-                               	if (ptt && isorion) {
-				    myisample=rf4isample;
-				    myqsample=rf4qsample;
-				}
+			      default:
+				myisample=0;
+				myqsample=0;
 				break;
 			    }
+			    if (ismetis && ptt && (k==1)) {
+				// METIS: TX DAC signal goes to RX2 when TXing
+				myisample=rf4isample;
+				myqsample=rf4qsample;
+			    }
+			    if (ishermes && ptt && (k==3)) {
+				// HERMES: TX DAC signal goes to RX4 when TXing
+				myisample=rf4isample;
+				myqsample=rf4qsample;
+			    }
+			    if (isorion && ptt && (k==4)) {
+				// ANGELIA and beyond: TX DAC signal goes to RX5 when TXing
+				myisample=rf4isample;
+				myqsample=rf4qsample;
+			    }
 			    *pointer++ = (myisample >> 16) & 0xFF;
 			    *pointer++ = (myisample >>  8) & 0xFF;
 			    *pointer++ = (myisample >>  0) & 0xFF;
diff --git a/main.c b/main.c
index ffb0242..55c9708 100644
--- a/main.c
+++ b/main.c
@@ -152,7 +152,7 @@ static int init(void *data) {
   char wisdom_directory[1024];
   int rc;
 
-  fprintf(stderr,"init\n");
+  //fprintf(stderr,"init\n");
 
   audio_get_cards();
 
@@ -221,17 +221,17 @@ fprintf(stderr,"width=%d height=%d\n", display_width, display_height);
 
 fprintf(stderr,"display_width=%d display_height=%d\n", display_width, display_height);
 
-  fprintf(stderr,"create top level window\n");
+  //fprintf(stderr,"create top level window\n");
   top_window = gtk_application_window_new (app);
   if(full_screen) {
-fprintf(stderr,"full screen\n");
+    fprintf(stderr,"full screen\n");
     gtk_window_fullscreen(GTK_WINDOW(top_window));
   }
   gtk_widget_set_size_request(top_window, display_width, display_height);
   gtk_window_set_title (GTK_WINDOW (top_window), "piHPSDR");
   gtk_window_set_position(GTK_WINDOW(top_window),GTK_WIN_POS_CENTER_ALWAYS);
   gtk_window_set_resizable(GTK_WINDOW(top_window), FALSE);
-  fprintf(stderr,"setting top window icon\n");
+  //fprintf(stderr,"setting top window icon\n");
   GError *error;
   if(!gtk_window_set_icon_from_file (GTK_WINDOW(top_window), "hpsdr.png", &error)) {
     fprintf(stderr,"Warning: failed to set icon for top_window\n");
@@ -253,41 +253,41 @@ fprintf(stderr,"full screen\n");
   //fixed=gtk_fixed_new();
   //gtk_container_add(GTK_CONTAINER(top_window), fixed);
 
-fprintf(stderr,"create grid\n");
+//fprintf(stderr,"create grid\n");
   grid = gtk_grid_new();
   gtk_widget_set_size_request(grid, display_width, display_height);
   gtk_grid_set_row_homogeneous(GTK_GRID(grid),FALSE);
   gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE);
-fprintf(stderr,"add grid\n");
+//fprintf(stderr,"add grid\n");
   gtk_container_add (GTK_CONTAINER (top_window), grid);
 
-fprintf(stderr,"create image\n");
+//fprintf(stderr,"create image\n");
   GtkWidget  *image=gtk_image_new_from_file("hpsdr.png");
-fprintf(stderr,"add image to grid\n");
+//fprintf(stderr,"add image to grid\n");
   gtk_grid_attach(GTK_GRID(grid), image, 0, 0, 1, 4);
 
-fprintf(stderr,"create pi label\n");
+//fprintf(stderr,"create pi label\n");
   char build[64];
   sprintf(build,"build: %s %s",build_date, version);
   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);
-fprintf(stderr,"add pi label to grid\n");
+//fprintf(stderr,"add pi label to grid\n");
   gtk_grid_attach(GTK_GRID(grid),pi_label,1,0,1,1);
 
-fprintf(stderr,"create build label\n");
+//fprintf(stderr,"create build label\n");
   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);
-fprintf(stderr,"add build label to grid\n");
+//fprintf(stderr,"add build label to grid\n");
   gtk_grid_attach(GTK_GRID(grid),build_date_label,1,1,1,1);
 
-fprintf(stderr,"create status\n");
+//fprintf(stderr,"create status\n");
   status=gtk_label_new("");
   gtk_label_set_justify(GTK_LABEL(status),GTK_JUSTIFY_LEFT);
   //gtk_widget_override_font(status, pango_font_description_from_string("FreeMono 18"));
   gtk_widget_show(status);
-fprintf(stderr,"add status to grid\n");
+//fprintf(stderr,"add status to grid\n");
   gtk_grid_attach(GTK_GRID(grid), status, 1, 3, 1, 1);
 
 /*
@@ -315,12 +315,12 @@ int main(int argc,char **argv) {
 
   sprintf(name,"org.g0orx.pihpsdr.pid%d",getpid());
 
-fprintf(stderr,"gtk_application_new: %s\n",name);
+//fprintf(stderr,"gtk_application_new: %s\n",name);
 
   pihpsdr=gtk_application_new(name, G_APPLICATION_FLAGS_NONE);
   g_signal_connect(pihpsdr, "activate", G_CALLBACK(activate_pihpsdr), NULL);
   status=g_application_run(G_APPLICATION(pihpsdr), argc, argv);
-fprintf(stderr,"exiting ...\n");
+  fprintf(stderr,"exiting ...\n");
   g_object_unref(pihpsdr);
   return status;
 }
diff --git a/midi.h b/midi.h
index 98a6191..50915a7 100644
--- a/midi.h
+++ b/midi.h
@@ -44,30 +44,41 @@
 
 //
 // MIDIaction encodes the "action" to be taken in Layer3
+// (sorted alphabetically)
 //
 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
+  ACTION_NONE=0,	// No-Op (unassigned key)
+  AGC,			// AGC level
+  AGCATTACK,		// AGC ATTACK (cycle fast/med/slow etc.)
+  ATT,			// Step attenuator or Programmable attenuator
+  AF_GAIN,		// AF gain
+  BAND_DOWN,		// cycle through bands downwards
+  BAND_UP,		// cycle through bands upwards
+  COMPRESS,		// TX compressor value
+  CTUN,			// CTUN on/off
+  FILTER_UP,		// cycle through filters upwards
+  FILTER_DOWN,		// cycle through filters downwards
+  MIC_VOLUME,		// MIC gain
+  LOCK,			// disable frequency changes
+  MODE_UP,		// cycle through modes upwards
+  MODE_DOWN,		// cycle through modes downwards
+  MOX,			// toggle "mox" state
+  NB,			// cycle through NoiseBlanker states (none, NB, NB2)
+  NR,			// cycle through NoiseReduction states (none, NR, NR2)
+  PRE,			// preamp on/off
+  PAN_HIGH,		// "high" value of current panadapter
+  PAN_LOW,		// "low" value of current panadapter
+  PS,			// PURESIGNAL on/off
+  RIT_CLEAR,		// clear RIT value
+  RIT_VAL,		// change RIT value
+  SPLIT,		// Split on/off
+  SWAP_VFO,		// swap VFO A/B frequency
+  TUNE,			// toggle "tune" state
+  TX_DRIVE,		// RF output power
+  VFO,			// change VFO frequency
+  VFO_A2B,		// VFO A -> B
+  VFO_B2A,		// VFO B -> A
+  VOX 			// VOX on/off
 };
 
 //
@@ -153,9 +164,9 @@ struct desc {
    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)
+   int		     delay;       // Wheel only: delay (msec) before next message is given upstream
    enum MIDIaction   action;	  // SDR "action" to generate
-   struct desc       *next;       // Next defined action for a controller/key with that note value.
+   struct desc       *next;       // Next defined action for a controller/key with that note value (NULL for end of list)
 };
 
 struct {
@@ -186,7 +197,8 @@ 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.
+// The implementation of DoTheMIDI is tightly bound to pihpsr and contains
+// tons of invocations of g_idle_add with routines from ext.c
 //
 
 void DoTheMidi(enum MIDIaction code, enum MIDItype type, int val);
diff --git a/midi.inp b/midi.inp
index 0e6ebca..cb0ddce 100644
--- a/midi.inp
+++ b/midi.inp
@@ -3,31 +3,72 @@
 #
 # 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)
+# fits best for radios with a programmable attenuator (0-31 dB).
+# The button (Key 2) also works for STEMlab and CHARLY25
+#
+# The preamp button (Key 1) only has function with STEMlab and CHARLY25
+#
+# Do not assign the "Key" with number 31. You will unintentionally
+# "push" it most of the times you change the VFO frequency.
+#
+# NOTE: we could just leave out all lines with ACTION=NONE. In this case
+#       a diagnostic message ("unknown MIDI event") is printed to stderr.
 #
 DEVICE=CMD PL-1
+#
+# Big Wheel and Big Slider
+#
 CTRL=31 WHEEL THR=59 61 63 65 67 69 ACTION=VFO           # Big wheel:    : main VFO knob
+KEY=31 ACTION=NONE                                       # Button integrated in the big wheel (do not assign)
 PITCH ACTION=AFGAIN                                      # Big slider    : AF gain
+#
+# 8 Knobs (top left).
+# Note that you can push each knob and this generates
+# Note On/Off messages for KEY=0...8 which we do not
+# assign: we want to glue labels on the Controller and therefore
+# we do not have two labels per knob.
+#
+CTRL=0 WHEEL THR=-1 -1 63 65 128 128 ACTION=ATT          # Knob 1        : RX att
+KEY=0                                ACTION=NONE         # Push Knob 1   : (unassigned)
+CTRL=1 WHEEL THR=-1 -1 63 65 128 128 ACTION=COMPRESS     # Knob 2        : TX compression
+KEY=1                                ACTION=NONE         # Push Knob 1   : (unassigned)
+CTRL=2 WHEEL THR=-1 -1 63 65 128 128 ACTION=RITVAL       # Knob 3        : RIT value
+KEY=2                                ACTION=NONE         # Push Knob 1   : (unassigned)
+CTRL=3 WHEEL THR=-1 -1 63 65 128 128 ACTION=PANLOW       # Knob 4        : Panadapter low
+KEY=4                                ACTION=NONE         # Push Knob 1   : (unassigned)
+CTRL=4 WHEEL THR=-1 -1 63 65 128 128 ACTION=AGC          # Knob 5        : AGC
+KEY=4                                ACTION=NONE         # Push Knob 1   : (unassigned)
+CTRL=5 WHEEL THR=-1 -1 63 65 128 128 ACTION=MICGAIN      # Knob 6        : MIC gain
+KEY=5                                ACTION=NONE         # Push Knob 1   : (unassigned)
+CTRL=6 WHEEL THR=-1 -1 63 65 128 128 ACTION=RFPOWER      # Knob 7        : TX drive
+KEY=6                                ACTION=NONE         # Push Knob 1   : (unassigned)
+CTRL=7 WHEEL THR=-1 -1 63 65 128 128 ACTION=FILTERUP     # Knob 8        : cycle through the filters
+KEY=7                                ACTION=NONE         # Push Knob 1   : (unassigned)
+#
+# 8 Keys (below the 8 Knobs)
+#
 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=18 ACTION=RITCLEAR 					 # Key 3         : Clear RIT value and disable RIT
+KEY=19 ACTION=CTUN 				 	 # Key 4         : toggle CTUN
+KEY=20 ACTION=NOISEBLANKER				 # Key 5         : cycle through NB settings
+KEY=21 ACTION=NOISEREDUCTION				 # Key 6         : cycle through NR settings
+KEY=22 ACTION=VOX 					 # Key 7         : toggle VOX
+KEY=23 ACTION=AGCATTACK				 	 # Key 8         : cycle AGC fast/medium/slow
+#
+# "other" keys
+# Note that the "DECK" key switches the MIDI channel of the device.
+#
 KEY=24 ACTION=TUNE                                       # LOAD    button: TUNE on/off
+KEY=25 ACTION=LOCK                                       # LOCK    button: Lock VFO(s)
+KEY=26 ACTION=PURESIGNAL                                 # DECK    button: toggle PURESIGNAL
 KEY=27 ACTION=SWAPVFO 					 # SCRATCH button: Swap VFOs A and B
+KEY=31 ACTION=NONE                                       # Button integrated in the big wheel (do not assign)
+KEY=32 ACTION=VFOA2B                                     # SYNC    button: Frequency VFO A -> VFO B
+KEY=33 ACTION=VFOB2A                                     # TAP     button: Frequency VFO B -> VFO A
 KEY=34 ACTION=MOX                                        # CUE     button: MOX on/off
+KEY=35 ACTION=SPLIT                                      # >||     button: toggle Split
 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
index 0d4d300..9145137 100644
--- a/midi2.c
+++ b/midi2.c
@@ -63,7 +63,6 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
 		    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;
@@ -75,6 +74,12 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
 	    desc=desc->next;
 	}
     }
+    if (!desc) {
+      // Nothing found. This is nothing to worry about, but log the key to stderr
+      if (event == MIDI_PITCH) fprintf(stderr, "Unassigned PitchBend Value=%d\n", val);
+      if (event == MIDI_NOTE ) fprintf(stderr, "Unassigned Key Note=%d Val=%d\n", note, val);
+      if (event == MIDI_CTRL ) fprintf(stderr, "Unassigned Controller Ctl=%d Val=%d\n", note, val);
+    }
 }
 
 /*
@@ -86,27 +91,38 @@ 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"},
+	{ AGCATTACK,   	"AGCATTACK"},
+        { ATT,          "ATT"},
         { BAND_DOWN,    "BANDDOWN"},
-        { FILTER_UP,    "FILTERUP"},
+        { BAND_UP,      "BANDUP"},
+        { COMPRESS,     "COMPRESS"},
+	{ CTUN,  	"CTUN"},
         { FILTER_DOWN,  "FILTERDOWN"},
-	{ MODE_UP,	"MODEUP"},
+        { FILTER_UP,    "FILTERUP"},
+	{ LOCK,    	"LOCK"},
+        { MIC_VOLUME,   "MICGAIN"},
 	{ MODE_DOWN,	"MODEDOWN"},
+	{ MODE_UP,	"MODEUP"},
+        { MOX,          "MOX"},
+	{ NB,    	"NOISEBLANKER"},
+	{ NR,    	"NOISEREDUCTION"},
+        { PAN_HIGH,     "PANHIGH"},
+        { PAN_LOW,      "PANLOW"},
+        { PRE,          "PREAMP"},
+	{ PS,    	"PURESIGNAL"},
+        { RIT_CLEAR,    "RITCLEAR"},
+        { RIT_VAL,      "RITVAL"},
+	{ SPLIT,  	"SPLIT"},
 	{ SWAP_VFO,	"SWAPVFO"},
+        { TUNE,         "TUNE"},
+        { TX_DRIVE,     "RFPOWER"},
+        { VFO,          "VFO"},
+	{ VFO_A2B,	"VFOA2B"},
+	{ VFO_B2A,	"VFOB2A"},
+	{ VOX,   	"VOX"},
+        { ACTION_NONE,  "NONE"},
         { ACTION_NONE,  NULL}
 };
 
@@ -118,7 +134,10 @@ static enum MIDIaction keyword2action(char *s) {
     int i=0;
 
     for (i=0; 1; i++) {
-	if (ActionTable[i].str == NULL) return ACTION_NONE;
+	if (ActionTable[i].str == NULL) {
+	  fprintf(stderr,"MIDI: action keyword %s NOT FOUND.\n", s);
+	  return ACTION_NONE;
+	}
 	if (!strcmp(s, ActionTable[i].str)) return ActionTable[i].action;
    }
    /* NOTREACHED */
@@ -170,8 +189,8 @@ void MIDIstartup() {
         continue; // nothing more in this line
       }
       chan=-1;  // default: any channel
-      lt3=lt2=lt1=0;
-      ut3=ut2=ut1=127;
+      lt3=lt2=lt1=-1;
+      ut3=ut2=ut1=128;
       onoff=0;
       event=EVENT_NONE;
       type=TYPE_NONE;
diff --git a/midi3.c b/midi3.c
index 8e0c33f..d654dca 100644
--- a/midi3.c
+++ b/midi3.c
@@ -18,6 +18,7 @@
 #include "new_menu.h"
 #include "sliders.h"
 #include "ext.h"
+#include "agc.h"
 #include "midi.h"
 
 void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
@@ -33,7 +34,9 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
 	    }
 	    break;    
 	case VFO: // only wheel supported
-	    if (type == MIDI_WHEEL) g_idle_add(ext_vfo_step, (gpointer)(uintptr_t) val);
+	    if (type == MIDI_WHEEL) {
+		g_idle_add(ext_vfo_step, (gpointer)(uintptr_t) val);
+	    }
 	    break;
 	case TUNE: // only key supported
 	    if (type == MIDI_KEY) {
@@ -150,18 +153,23 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
 		}
 	    }
 	    break;
-	case RIT_ONOFF:  // only key supported
+	case RIT_CLEAR:  // only key supported
 	    if (type == MIDI_KEY) {
-		vfo[active_receiver->id].rit_enabled = !vfo[active_receiver->id].rit_enabled;
+		// this clears the RIT value and disables RIT
+		vfo[active_receiver->id].rit = new;
+		vfo[active_receiver->id].rit_enabled = 0;
 	        g_idle_add(ext_vfo_update, NULL);
 	    }
 	    break;
 	case RIT_VAL:	// only wheel supported
 	    if (type == MIDI_WHEEL) {
+		// This changes the RIT value. If a value of 0 is reached,
+		// RIT is disabled
 		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;
+		vfo[active_receiver->id].rit_enabled = (new != 0);
 	        g_idle_add(ext_vfo_update, NULL);
 	    }
 	    break;
@@ -196,6 +204,8 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
 		}
 		g_idle_add(ext_update_att_preamp, NULL);
 	    } else {
+		// Note that these preamps are not present in most
+		// SDR hardware.
 		new=active_receiver->preamp+1;
 		if (new > 1) new=0;
 		active_receiver->preamp= (new == 1);
@@ -227,8 +237,104 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
 	    }
 	    break;
 	case COMPRESS:
+	    // Use values in the range 0 ... 20
+	    if (type == MIDI_WHEEL) {
+              dnew=transmitter->compressor_level + val;
+	      if (dnew > 20.0) dnew=20.0;
+	      if (dnew < 0 ) dnew=0;
+	    } else if (type == MIDI_KNOB){
+	      dnew=(20.0*val)/100.0;
+	    } else {
+		break;
+	    }
+	    transmitter->compressor_level=dnew;
+	    if (dnew < 0.5) transmitter->compressor=0;
+	    if (dnew > 0.5) transmitter->compressor=1;
+	    g_idle_add(ext_vfo_update, NULL);
+	    break;
+	case NB:
+	    // cycle through NoiseBlanker settings
+	    if (active_receiver->nb) {
+		active_receiver->nb = 0;
+		active_receiver->nb2= 1;
+	    } else if (active_receiver->nb2) {
+		active_receiver->nb = 0;
+		active_receiver->nb2= 0;
+	    } else {
+		active_receiver->nb = 1;
+		active_receiver->nb2= 0;
+	    }
+	    g_idle_add(ext_vfo_update, NULL);
+	    break;
+	case NR:
+	    // cycle through NoiseReduction settings
+	    if (active_receiver->nr) {
+		active_receiver->nr = 0;
+		active_receiver->nr2= 1;
+	    } else if (active_receiver->nr2) {
+		active_receiver->nr = 0;
+		active_receiver->nr2= 0;
+	    } else {
+		active_receiver->nr = 1;
+		active_receiver->nr2= 0;
+	    }
+	    g_idle_add(ext_vfo_update, NULL);
+	    break;
+	case VOX:
+	    // toggle VOX
+	    vox_enabled = !vox_enabled;
+	    g_idle_add(ext_vfo_update, NULL);
+	    break;
+	case CTUN:
+	    // toggle CTUN
+	    new=active_receiver->id;
+	    if(!vfo[new].ctun) {
+		vfo[new].ctun=1;
+		vfo[new].offset=0;
+	    } else {
+		vfo[new].ctun=0;
+	    }
+	    vfo[new].ctun_frequency=vfo[new].frequency;
+	    set_offset(active_receiver,vfo[new].offset);
+	    g_idle_add(ext_vfo_update, NULL);
+	    break;
+	case PS:
+	    // toggle PURESIGNAL
+	    new=!(transmitter->puresignal);
+	    g_idle_add(ext_tx_set_ps,(gpointer) (uintptr_t) new);
+	    break;
+	case SPLIT:
+	    // toggle split mode
+	    if(!split) {
+		split=1;
+		tx_set_mode(transmitter,vfo[VFO_B].mode);
+	    } else {
+		split=0;
+		tx_set_mode(transmitter,vfo[VFO_A].mode);
+	    }
+	    g_idle_add(ext_vfo_update, NULL);
+	    break;
+	case VFO_A2B:
+	    g_idle_add(ext_vfo_a_to_b, NULL);
+	    break;
+	case VFO_B2A:
+	    g_idle_add(ext_vfo_b_to_a, NULL);
+	    break;
+	case LOCK:
+	    locked=!locked;
+	    g_idle_add(ext_vfo_update, NULL);
+	    break;
+	case AGCATTACK:
+	    new=active_receiver->agc + 1;
+	    if (new > AGC_FAST) new=0;
+	    active_receiver->agc=new;
+	    g_idle_add(ext_vfo_update, NULL);
+	    break;
 	case ACTION_NONE:
-            fprintf(stderr,"Unimplemented in DoTheMidi: A=%d T=%d val=%d\n", action, type, val);
+	    // No error message, this is the "official" action for un-used controller buttons.
 	    break;
+	default:
+	    // This means we have forgotten to implement an action
+	    fprintf(stderr,"Unimplemented MIDI action: A=%d\n", (int) action);
     }
 }
diff --git a/old_protocol.c b/old_protocol.c
index 3ef7d43..b4a65a3 100644
--- a/old_protocol.c
+++ b/old_protocol.c
@@ -565,7 +565,6 @@ static void process_ozy_input_buffer(unsigned char  *buffer) {
   int nreceivers;
 
 #ifdef PURESIGNAL
-    // DL1YCF:
     // for PureSignal, the number of receivers needed is hard-coded below.
     // we need at least 3 (for RX), and up to 5 for Orion2 boards, since
     // the TX DAC channel is hard-wired to RX5.
@@ -849,7 +848,6 @@ void ozy_send_buffer() {
   int nreceivers;
 
 #ifdef PURESIGNAL
-    // DL1YCF:
     // for PureSignal, the number of receivers needed is hard-coded below.
     // we need at least 3 (for RX), and up to 5 for Orion2 boards, since
     // the TX DAC channel is hard-wired to RX5.
@@ -957,22 +955,23 @@ void ozy_send_buffer() {
     // Upon TX, we might have to activate a different RX path for the
     // attenuated feedback signal. Use feedback_antenna == 0, if
     // the feedback signal is routed automatically/internally
+    // If feedback is to the second ADC, leave RX1 ANT settings untouched
     //
-    if (isTransmitting() && transmitter->puresignal) i=receiver[PS_RX_FEEDBACK]->feedback_antenna;
+    if (isTransmitting() && transmitter->puresignal && receiver[PS_RX_FEEDBACK]->adc == 0) i=receiver[PS_RX_FEEDBACK]->feedback_antenna;
 #endif
     switch(i) {
-      case 3:  // Alex: RX2 IN, ANAN: EXT1, ANAN7000: EXT
-        output_buffer[C3]|=0xC0;
+      case 3:  // Alex: RX2 IN, ANAN: EXT1, ANAN7000: still uses internal feedback 
+        output_buffer[C3]|=0x80;
         break;
       case 4:  // Alex: RX1 IN, ANAN: EXT2, ANAN7000: RX BYPASS
         output_buffer[C3]|=0xA0;
         break;
       default:
+	// RX1_OUT and RX1_ANT bits remain zero
         break;
     }
 
 
-// TODO - add Alex TX relay, duplex, receivers Mercury board frequency
     output_buffer[C4]=0x04;  // duplex
 
     // 0 ... 7 maps on 1 ... 8 receivers
@@ -1051,8 +1050,8 @@ void ozy_send_buffer() {
           output_buffer[C0]=0x04+(current_rx*2);
 #ifdef PURESIGNAL
           int v=receiver[current_rx/2]->id;
-	  // DL1YCF: for the "last" receiver, v is out of range. In this case,
-	  //         use TX frequency also while receiving
+	  // for the "last" receiver, v is out of range. In this case,
+	  // use TX frequency also while receiving
           if((isTransmitting() && transmitter->puresignal) || (v >= MAX_VFOS)) {
             long long txFrequency;
             if(active_receiver->id==VFO_A) {
@@ -1178,7 +1177,7 @@ void ozy_send_buffer() {
         output_buffer[C4]=0x00;
   
         if(radio->device==DEVICE_HERMES || radio->device==DEVICE_ANGELIA || radio->device==DEVICE_ORION || radio->device==DEVICE_ORION2) {
-	  // DL1YCF: if attenuation is zero, then disable attenuator
+	  // if attenuation is zero, then disable attenuator
 	  i = adc_attenuation[receiver[0]->adc] & 0x1F;
           if (i >0) output_buffer[C4]=0x20| i;
         } else {
@@ -1193,7 +1192,7 @@ void ozy_send_buffer() {
         output_buffer[C1]=0x00;
         if(receivers==2) {
           if(radio->device==DEVICE_HERMES || radio->device==DEVICE_ANGELIA || radio->device==DEVICE_ORION || radio->device==DEVICE_ORION2) {
-	    // DL1YCF: if attenuation is zero, then disable attenuator
+	    // if attenuation is zero, then disable attenuator
 	    i = adc_attenuation[receiver[1]->adc] & 0x1F;
             if (i > 0) output_buffer[C1]=0x20|i;
           }
@@ -1209,33 +1208,20 @@ void ozy_send_buffer() {
         // need to add tx attenuation and rx ADC selection
         output_buffer[C0]=0x1C;
         output_buffer[C1]=0x00;
+        output_buffer[C2]=0x00;
 #ifdef PURESIGNAL
-
-	// The whole setup here implicitly assumes
-	// that receiver[0]->adc is 0 and receiver[1]->adc is 1
-
-	// This is the correct setting for DEVICE_METIS and DEVICE_HERMES
-        output_buffer[C1]|=receiver[0]->adc;		// RX1 bound to ADC0
-        output_buffer[C1]|=(receiver[0]->adc<<2);	// RX2 bound to ADC0
-        output_buffer[C1]|=receiver[1]->adc<<4;		// RX3 bound to ADC1
-
-	// DL1YCF:
-	// For Orion, Angelia and OrionMk2 RX4 must show the FeedBack signal.
-        // In most cases this is routed back to RX1 so we need ADC0 for RX4
-	// Since I could test this only on my ANAN7000, I changed to code
-	// to associate RX4 with ADC0 only on Orion2 -- but I guess this will
-	// be necessary for other SDRs as well.
-
-        if (device == DEVICE_ORION2) {
-          output_buffer[C1]|=(receiver[0]->adc<<6);   // This works on ANAN7000
-	} else {
-          output_buffer[C1]|=(receiver[1]->adc<<6);   // Does this work for somebody?
+        // if n_adc == 1, there is only a single ADC, so we can leave everything
+        // set to zero
+	if (n_adc  > 1) {
+	    // Angelia, Orion, Orion2 have two ADCs, so we use the ADC settings from the menu
+            output_buffer[C1]|=receiver[0]->adc;			// RX1 bound to ADC of first receiver
+            output_buffer[C1]|=(receiver[1]->adc<<2);			// RX2 actually unsused with PURESIGNAL
+            output_buffer[C1]|=receiver[1]->adc<<4;			// RX3 bound to ADC of second receiver
+            output_buffer[C1]|=(receiver[PS_RX_FEEDBACK]->adc<<6);	// RX4 is PS_RX_Feedbacka
+									// Usually ADC0, but if feedback is to
+									// RX2 input it must be ADC1 (see ps_menu.c)
+	    // RX5 is hard-wired to the TX DAC and needs no ADC setting.
 	}
-        output_buffer[C2]=0x00;
-        if(transmitter->puresignal) {
-	  // This should not be necessary since RX5 is hard-wired to the TX DAC on TX
-          output_buffer[C2]|=receiver[2]->adc;
-        }
 #else
         output_buffer[C1]|=receiver[0]->adc;
         output_buffer[C1]|=(receiver[1]->adc<<2);
@@ -1282,8 +1268,19 @@ void ozy_send_buffer() {
       case 10:
         output_buffer[C0]=0x24;
         output_buffer[C1]=0x00;
+
         if(isTransmitting()) {
-          output_buffer[C1]|=0x80; // ground RX2 on transmit, bit0-6 are Alex2 filters
+#ifdef PURESIGNAL
+	  // If we are using the RX2 jack for PURESIGNAL RX feedback, then we MUST NOT ground
+	  // the ADC2 input upon TX.
+	  if (transmitter->puresignal && receiver[PS_RX_FEEDBACK]->adc == 1) {
+	    // Note that this statement seems to have no effect since
+	    // one cannot goto to "manual filter setting" for the ALEX2 board individually
+            output_buffer[C1]|=0x40; // Set ADC2 filter board to "ByPass"
+	  } else
+#endif
+
+            output_buffer[C1]|=0x80; // ground RX2 on transmit, bit0-6 are Alex2 filters
         }
         output_buffer[C2]=0x00;
         if(receiver[0]->alex_antenna==5) { // XVTR
@@ -1453,7 +1450,7 @@ static void metis_restart() {
   current_rx=0;
   command=1;
 #else
-  // DL1YCF this is the original code, which does not do what it pretends ....
+  // DL1YCF: this is the original code, which does not do what it pretends ....
   // send commands twice
   command=1;
   do {
diff --git a/portaudio.c b/portaudio.c
index bd0b987..a1ece79 100644
--- a/portaudio.c
+++ b/portaudio.c
@@ -1,9 +1,12 @@
 #ifdef PORTAUDIO
 //
-// DL1YCF: if PortAudio is NOT used, this file is empty, and audio.c
-//         is used instead.
-//         Here we also implement two (hopefully useful) functions:
-//         - a dummy two-tone 'Microphone' device
+// Contribution from DL1YCF (Christoph van Wullen)
+//
+// Alternate "audio" module using PORTAUDIO instead of ALSA
+// (e.g. on MacOS)
+//
+// If PortAudio is NOT used, this file is empty, and audio.c
+// is used instead.
 //
 
 #include <gtk/gtk.h>
diff --git a/ps_menu.c b/ps_menu.c
index 5008ff0..5213ed3 100644
--- a/ps_menu.c
+++ b/ps_menu.c
@@ -106,15 +106,8 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d
 }
 
 //
-//
-// DL1YCF: had repeated seg-faults, possibly this is not thread-safe.
-//         executing it at regular intervals in the glib main loop
-//         does the same thing.
-//         We now call this function every 100 msec, therefore we can
-//         adjust the tx attenuation upon each invocation.
-//         The massive alloc/free of the label is completely unnecessary
-//         and has been removed, the small string to be displayed is
-//         prepeared in "label".
+// This is called every 100 msec and therefore
+// must be a state machine
 //
 static int info_thread(gpointer arg) {
   static int info[INFO_SIZE];
@@ -264,9 +257,24 @@ static int info_thread(gpointer arg) {
     return TRUE;
 }
 
+//
+// Set "RX1 ANT", "RX1 OUT", and ADC settings for the PS feedback signal
+//
 static void ps_ant_cb(GtkWidget *widget, gpointer data) {
+  int val = (int) (uintptr_t) data;
   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
-    receiver[PS_RX_FEEDBACK]->feedback_antenna = (int) (uintptr_t) data;
+    switch (val) {
+      case 0:	// AUTO (Internal), feedback goes to first ADC
+      case 3:	// EXT1,            feedback goes to first ADC
+      case 4:	// EXT2,            feedback goes to first ADC
+	receiver[PS_RX_FEEDBACK]->feedback_antenna = (int) (uintptr_t) data;
+	receiver[PS_RX_FEEDBACK]->adc              = 0;
+	break;
+      case 99:	// RX2,              feedback goes to second ADC
+	receiver[PS_RX_FEEDBACK]->feedback_antenna = 0;
+	receiver[PS_RX_FEEDBACK]->adc              = 1;
+	break;
+    }
   }
 }
 
@@ -276,6 +284,7 @@ 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));
+  // switching OFF auto sets the attenuation to zero
   if(!transmitter->auto_on) {
     transmitter->attenuation=0;
   }
@@ -283,6 +292,13 @@ static void auto_cb(GtkWidget *widget, gpointer data) {
 }
 
 static void resume_cb(GtkWidget *widget, gpointer data) {
+  // Set the attenuation to zero if auto-adjusting and resuming.
+  // A very high attenuation value here could lead to no PS calculation
+  // done in WDSP, and hence no attenuation adjustment.
+  // If not auto-adjusting, do not change attenuation value.
+  if(transmitter->auto_on) {
+    transmitter->attenuation=0;
+  }
   SetPSControl(transmitter->id, 0, 0, 1, 0);
 }
 
@@ -297,7 +313,6 @@ static void feedback_cb(GtkWidget *widget, gpointer data) {
 }
 
 static void reset_cb(GtkWidget *widget, gpointer data) {
-  transmitter->attenuation=0;
   SetPSControl(transmitter->id, 1, 0, 0, 0);
 }
 
@@ -398,6 +413,14 @@ void ps_menu(GtkWidget *parent) {
   row++;
   col=0;
 
+  //
+  // Selection of feedback path for PURESIGNAL
+  //
+  // AUTO		Using internal feedback (to ADC0)
+  // EXT1		Using EXT1 jacket (to ADC0), ANAN-7000: still uses AUTO
+  // EXT2		Using EXT2 jacket (to ADC0), ANAN-7000: "EXT2 is called RX Bypass"
+  // RX2		Using RX2  jacket (to ADC1)
+  //
   GtkWidget *ps_ant_label=gtk_label_new("PS FeedBk ANT:");
   gtk_widget_show(ps_ant_label);
   gtk_grid_attach(GTK_GRID(grid), ps_ant_label, col, row, 1, 1);
@@ -405,26 +428,39 @@ void ps_menu(GtkWidget *parent) {
 
   GtkWidget *ps_ant_auto=gtk_radio_button_new_with_label(NULL,"AUTO");
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_auto), 
-    (receiver[PS_RX_FEEDBACK]->feedback_antenna!=3) && (receiver[PS_RX_FEEDBACK]->feedback_antenna!=4));
+    (receiver[PS_RX_FEEDBACK]->feedback_antenna == 0) && (receiver[PS_RX_FEEDBACK]->adc == 0));
   gtk_widget_show(ps_ant_auto);
   gtk_grid_attach(GTK_GRID(grid), ps_ant_auto, col, row, 1, 1);
   g_signal_connect(ps_ant_auto,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 0);
   col++;
 
   GtkWidget *ps_ant_ext1=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ps_ant_auto),"EXT1");
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext1), receiver[PS_RX_FEEDBACK]->feedback_antenna==3);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext1),
+    (receiver[PS_RX_FEEDBACK]->feedback_antenna==3) && (receiver[PS_RX_FEEDBACK]->adc == 0));
   gtk_widget_show(ps_ant_ext1);
   gtk_grid_attach(GTK_GRID(grid), ps_ant_ext1, col, row, 1, 1);
   g_signal_connect(ps_ant_ext1,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 3);
   col++;
 
   GtkWidget *ps_ant_ext2=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ps_ant_auto),"EXT2");
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext2), receiver[PS_RX_FEEDBACK]->feedback_antenna==4);
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext2),
+    (receiver[PS_RX_FEEDBACK]->feedback_antenna==4) && (receiver[PS_RX_FEEDBACK]->adc == 0));
   gtk_widget_show(ps_ant_ext2);
   gtk_grid_attach(GTK_GRID(grid), ps_ant_ext2, col, row, 1, 1);
   g_signal_connect(ps_ant_ext2,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 4);
+  col++;
+
+  if (n_adc > 1) {
+    // If there are two ADCs, we may choose to use the second ADC for PS_RX_FEEDBACK
+    GtkWidget *ps_ant_rx2=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ps_ant_auto),"RX2");
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_rx2), receiver[PS_RX_FEEDBACK]->adc==1);
+    gtk_widget_show(ps_ant_rx2);
+    gtk_grid_attach(GTK_GRID(grid), ps_ant_rx2, col, row, 1, 1);
+    g_signal_connect(ps_ant_rx2,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 99);
+  }
 
   row++;
+
   col=0;
   feedback_l=gtk_label_new("Feedback Lvl");
   gtk_widget_show(feedback_l);
@@ -518,9 +554,11 @@ void ps_menu(GtkWidget *parent) {
 
 // DL1YCF: This is not the default setting,
 // but in my experience in behaves better in difficult situations.
-  ints=8;
-  spi=512;
-  ampdelay=100e-9;
+// This is commented out because it is not recommended by the
+// WDSP manual.
+//  ints=8;
+//  spi=512;
+//  ampdelay=100e-9;
 
   SetPSIntsAndSpi(transmitter->id, ints, spi);
   SetPSStabilize(transmitter->id, stbl);
diff --git a/radio.c b/radio.c
index 74a4445..b646170 100644
--- a/radio.c
+++ b/radio.c
@@ -294,6 +294,7 @@ double vox_hang=250.0;
 int vox=0;
 int CAT_cw_is_active=0;
 int cw_key_hit=0;
+int n_adc=1;
 
 int diversity_enabled=0;
 double i_rotate[2]={1.0,1.0};
@@ -327,8 +328,8 @@ void reconfigure_radio() {
     } else {
       gtk_fixed_move(GTK_FIXED(fixed),sliders,0,y);
     }
-    gtk_widget_show_all(sliders);  // DL1YCF this shows both C25 and Alex ATT/Preamp sliders
-    att_type_changed();            // DL1YCF added here to hide the „wrong“ ones.
+    gtk_widget_show_all(sliders);  // ... this shows both C25 and Alex ATT/Preamp sliders
+    att_type_changed();            // ... and this hides the „wrong“ ones.
   } else {
     if(sliders!=NULL) {
       gtk_container_remove(GTK_CONTAINER(fixed),sliders); 
@@ -421,7 +422,7 @@ void start_radio() {
                           radio->info.network.mac_address[5],
                           radio->info.network.interface_name);
 
-fprintf(stderr,"title: length=%d\n", (int)strlen(text));
+//fprintf(stderr,"title: length=%d\n", (int)strlen(text));
 
   gtk_window_set_title (GTK_WINDOW (top_window), text);
 
@@ -482,6 +483,41 @@ fprintf(stderr,"title: length=%d\n", (int)strlen(text));
       break;
   }
  
+  // Code moved here from rx_menu because n_adc is of general interest:
+  // Determine Number of ADCs.
+  switch(protocol) {
+    case ORIGINAL_PROTOCOL:
+      switch(device) {
+        case DEVICE_METIS:
+          n_adc=1;  // No support for multiple MERCURY cards on a single ATLAS bus.
+          break;
+        case DEVICE_HERMES:
+        case DEVICE_HERMES_LITE:
+          n_adc=1;
+          break;
+        default:
+          n_adc=2;
+          break;
+      }
+      break;
+    case NEW_PROTOCOL:
+      switch(device) {
+        case NEW_DEVICE_ATLAS:
+          n_adc=1; // No support for multiple MERCURY cards on a single ATLAS bus.
+          break;
+        case NEW_DEVICE_HERMES:
+        case NEW_DEVICE_HERMES2:
+        case NEW_DEVICE_HERMES_LITE:
+          n_adc=1;
+          break;
+        default:
+          n_adc=2;
+          break;
+      }
+      break;
+    default:
+      break;
+  }
 
   adc_attenuation[0]=0;
   adc_attenuation[1]=0;
@@ -531,15 +567,22 @@ fprintf(stderr,"title: length=%d\n", (int)strlen(text));
   rx_height=rx_height/receivers;
 
 
-fprintf(stderr,"Create %d receivers: height=%d\n",receivers,rx_height);
+//fprintf(stderr,"Create %d receivers: height=%d\n",receivers,rx_height);
   for(i=0;i<RECEIVERS;i++) {
-    receiver[i]=create_receiver(i, buffer_size, fft_size, display_width, updates_per_second, display_width, rx_height);
-    setSquelch(receiver[i]);
+    // A receiver may be un-unsed upon program start, but can be activated through the radio menu later.
+    // Create it here with height 0. Make sure to call g_object_ref for this one as well,
+    // this cures the seg-faults observed when switching beteen 1 and 2 RX in the radio menu.
     if(i<receivers) {
-      receiver[i]->x=0;
-      receiver[i]->y=y;
-      gtk_fixed_put(GTK_FIXED(fixed),receiver[i]->panel,0,y);
-      g_object_ref((gpointer)receiver[i]->panel);
+      receiver[i]=create_receiver(i, buffer_size, fft_size, display_width, updates_per_second, display_width, rx_height);
+    } else {
+      receiver[i]=create_receiver(i, buffer_size, fft_size, display_width, updates_per_second, display_width, 0        );
+    }
+    setSquelch(receiver[i]);
+    receiver[i]->x=0;
+    receiver[i]->y=y;
+    gtk_fixed_put(GTK_FIXED(fixed),receiver[i]->panel,0,y);
+    g_object_ref((gpointer)receiver[i]->panel);
+    if (i<receivers) {
       set_displaying(receiver[i],1);
       y+=rx_height;
     } else {
@@ -553,7 +596,7 @@ fprintf(stderr,"Create %d receivers: height=%d\n",receivers,rx_height);
 
   active_receiver=receiver[0];
 
-  fprintf(stderr,"Create transmitter\n");
+  //fprintf(stderr,"Create transmitter\n");
   transmitter=create_transmitter(CHANNEL_TX, buffer_size, fft_size, updates_per_second, display_width, tx_height);
   transmitter->x=0;
   transmitter->y=VFO_HEIGHT;
@@ -604,7 +647,7 @@ fprintf(stderr,"Create %d receivers: height=%d\n",receivers,rx_height);
 //#endif
 
   if(display_sliders) {
-fprintf(stderr,"create sliders\n");
+//fprintf(stderr,"create sliders\n");
     sliders = sliders_init(display_width,SLIDERS_HEIGHT);
     gtk_fixed_put(GTK_FIXED(fixed),sliders,0,y);
     y+=SLIDERS_HEIGHT;
@@ -664,20 +707,21 @@ void disable_rigctl() {
  
 
 void radio_change_receivers(int r) {
+  // The button in the radio menu will call this function even if the
+  // number of receivers has not changed.
+  if (receivers == r) return;
   fprintf(stderr,"radio_change_receivers: from %d to %d\n",receivers,r);
   switch(r) {
     case 1:
-      if(receivers==2) {
-        set_displaying(receiver[1],0);
-        gtk_container_remove(GTK_CONTAINER(fixed),receiver[1]->panel);
-      }
-      receivers=1;
-      break;
+	set_displaying(receiver[1],0);
+	gtk_container_remove(GTK_CONTAINER(fixed),receiver[1]->panel);
+	receivers=1;
+	break;
     case 2:
-      gtk_fixed_put(GTK_FIXED(fixed),receiver[1]->panel,0,0);
-      set_displaying(receiver[1],1);
-      receivers=2;
-      break;
+	gtk_fixed_put(GTK_FIXED(fixed),receiver[1]->panel,0,0);
+	set_displaying(receiver[1],1);
+	receivers=2;
+	break;
   }
   reconfigure_radio();
   active_receiver=receiver[0];
@@ -688,16 +732,21 @@ void radio_change_receivers(int r) {
 
 void radio_change_sample_rate(int rate) {
   int i;
+  
   switch(protocol) {
     case ORIGINAL_PROTOCOL:
-      old_protocol_stop();
-      for(i=0;i<receivers;i++) {
-        receiver_change_sample_rate(receiver[i],rate);
-      }
-      old_protocol_set_mic_sample_rate(rate);
-      old_protocol_run();
+      // The radio menu calls this function even if the sample rate
+      // has not changed. Do nothing in this case.
+      if (receiver[0]->sample_rate != rate) {
+        old_protocol_stop();
+        for(i=0;i<receivers;i++) {
+          receiver_change_sample_rate(receiver[i],rate);
+        }
+        old_protocol_set_mic_sample_rate(rate);
+        old_protocol_run();
 #ifdef PURESIGNAL
-      tx_set_ps_sample_rate(transmitter,rate);
+        tx_set_ps_sample_rate(transmitter,rate);
+      }
 #endif
       break;
 #ifdef LIMESDR
@@ -876,10 +925,11 @@ void setTune(int state) {
       }
       pre_tune_mode=mode;
 
-      // DL1YCF: in USB/DIGU/DSB, tune 1000 Hz above carrier
-      //         in LSB/DIGL,     tune 1000 Hz below carrier
-      //         all other (CW, AM, FM): tune on carrier freq.
-      //         Note: do not look at CW sidetone freq here.
+      //
+      // in USB/DIGU/DSB, tune 1000 Hz above carrier
+      // in LSB/DIGL,     tune 1000 Hz below carrier
+      // all other (CW, AM, FM): tune on carrier freq.
+      //
       switch(mode) {
         case modeLSB:
         case modeDIGL:
@@ -1524,7 +1574,7 @@ void calculate_display_average(RECEIVER *rx) {
 void set_filter_type(int filter_type) {
   int i;
 
-  fprintf(stderr,"set_filter_type: %d\n",filter_type);
+  //fprintf(stderr,"set_filter_type: %d\n",filter_type);
   for(i=0;i<RECEIVERS;i++) {
     receiver[i]->low_latency=filter_type;
     RXASetMP(receiver[i]->id, filter_type);
@@ -1536,7 +1586,7 @@ void set_filter_type(int filter_type) {
 void set_filter_size(int filter_size) {
   int i;
 
-  fprintf(stderr,"set_filter_size: %d\n",filter_size);
+  //fprintf(stderr,"set_filter_size: %d\n",filter_size);
   for(i=0;i<RECEIVERS;i++) {
     receiver[i]->fft_size=filter_size;
     RXASetNC(receiver[i]->id, filter_size);
diff --git a/radio.h b/radio.h
index 8a7656d..8e8c349 100644
--- a/radio.h
+++ b/radio.h
@@ -244,6 +244,7 @@ extern double vox_hang;
 extern int vox;
 extern int CAT_cw_is_active;
 extern int cw_key_hit;
+extern int n_adc;
 
 extern int diversity_enabled;
 extern double i_rotate[2];
diff --git a/radio_menu.c b/radio_menu.c
index 05cb830..9d40fdb 100644
--- a/radio_menu.c
+++ b/radio_menu.c
@@ -101,14 +101,14 @@ static void load_filters(void) {
 
   if(filter_board==ALEX || filter_board==APOLLO) {
     BAND *band=band_get_current_band();
-    BANDSTACK_ENTRY* entry=bandstack_entry_get_current();
-    setFrequency(entry->frequency);
+    // mode and filters have nothing to do with the filter board
+    //BANDSTACK_ENTRY* entry=bandstack_entry_get_current();
+    //setFrequency(entry->frequency);
     //setMode(entry->mode);
-    set_mode(active_receiver,entry->mode);
-    FILTER* band_filters=filters[entry->mode];
-    FILTER* band_filter=&band_filters[entry->filter];
-    //setFilter(band_filter->low,band_filter->high);
-    set_filter(active_receiver,band_filter->low,band_filter->high);
+    //set_mode(active_receiver,entry->mode);
+    //FILTER* band_filters=filters[entry->mode];
+    //FILTER* band_filter=&band_filters[entry->filter];
+    //set_filter(active_receiver,band_filter->low,band_filter->high);
     if(active_receiver->id==0) {
       set_alex_rx_antenna(band->alexRxAntenna);
       set_alex_tx_antenna(band->alexTxAntenna);
diff --git a/receiver.c b/receiver.c
index bdaf835..9a1ed09 100644
--- a/receiver.c
+++ b/receiver.c
@@ -480,7 +480,7 @@ fprintf(stderr,"reconfigure_receiver: panadapter_init: width:%d height:%d\n",rx-
       gtk_fixed_put(GTK_FIXED(rx->panel),rx->panadapter,0,y);
     } else {
        // set the size
-fprintf(stderr,"reconfigure_receiver: panadapter set_size_request: width:%d height:%d\n",rx->width,height);
+//fprintf(stderr,"reconfigure_receiver: panadapter set_size_request: width:%d height:%d\n",rx->width,height);
       gtk_widget_set_size_request(rx->panadapter, rx->width, height);
       // move the current one
       gtk_fixed_move(GTK_FIXED(rx->panel),rx->panadapter,0,y);
diff --git a/rigctl.c b/rigctl.c
index 81b40dc..052218f 100644
--- a/rigctl.c
+++ b/rigctl.c
@@ -2216,7 +2216,7 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
         else if((strcmp(cmd_str,"KY")==0) && (zzid_flag == 0))
 				    { 
 					if (cw_busy < 0) cat_cw_seen=1;
-					// DL1YCF:
+					//
 					// Hamlib produces timeout errors if we are busy here for
 					// seconds. Therefore we just move the data into a buffer
 					// that is processed by a separate thread.
@@ -2228,9 +2228,10 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
 					//  - if we can accept new data (buffer space available) : "KY0;"
 					//  - if buffer is full: "KY1;"
 					//
-					// Note: cw_buse == -1 indicates a "purge KY" situation, where
+					// Note: cw_busy == -1 indicates a "purge KY" situation, where
 					//       all KY commands are accepted and data is discared silently
 					//       In this case cw_busy is left untouched here.
+					//
                                         if (len <= 2) {
 					    if (cw_busy == 1) {
 						send_resp(client_sock,"KY1;");
diff --git a/rx_menu.c b/rx_menu.c
index 54f7e0e..4cff08f 100644
--- a/rx_menu.c
+++ b/rx_menu.c
@@ -112,7 +112,6 @@ static void mute_radio_cb(GtkWidget *widget, gpointer data) {
 }
 
 //
-// DL1YCF:
 // possible the device has been changed:
 // call audo_close_output with old device, audio_open_output with new one
 //
@@ -290,45 +289,8 @@ void rx_menu(GtkWidget *parent) {
     }
   }
 
-  // Number of ADCs. 
-  int n_adc=1;
-  switch(protocol) {
-    case ORIGINAL_PROTOCOL:
-      switch(device) {
-        case DEVICE_METIS:
-          n_adc=1;  // No support for multiple MERCURY cards on a single ATLAS bus.
-          break;
-        case DEVICE_HERMES:
-        case DEVICE_HERMES_LITE:
-          n_adc=1;
-          break;
-        default: 
-          n_adc=2;
-          break;
-      }
-      break;
-    case NEW_PROTOCOL:
-      switch(device) {
-        case NEW_DEVICE_ATLAS:
-          n_adc=1; // No support for multiple MERCURY cards on a single ATLAS bus.
-          break;
-        case NEW_DEVICE_HERMES:
-        case NEW_DEVICE_HERMES2:
-        case NEW_DEVICE_HERMES_LITE:
-          n_adc=1;
-          break;
-        default:
-          n_adc=2;
-          break;
-      }
-      break;
-    default:
-      break;
-  }
-
   // If there is more than one ADC, let the user associate an ADC
-  // with the current receiver. Note: for PURESIGNAL, ADC0 has to
-  // be associated with the first receiver.
+  // with the current receiver.
   if(n_adc>1) {
     for(i=0;i<n_adc;i++) {
       sprintf(label,"ADC-%d",i);
diff --git a/sliders.c b/sliders.c
index 5193be6..d1b7a1c 100644
--- a/sliders.c
+++ b/sliders.c
@@ -230,8 +230,8 @@ static gboolean load_att_type_cb(gpointer data) {
 static void c25_att_combobox_changed(GtkWidget *widget, gpointer data) {
   int val = atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)));
   if (active_receiver->id == 0) {
-    //DL1YCF: this button is only valid for the first receiver
-    //        store attenuation, such that in meter.c the correct level is displayed
+    // this button is only valid for the first receiver
+    // store attenuation, such that in meter.c the correct level is displayed
     adc_attenuation[active_receiver->adc] = 12*val;
     set_alex_attenuation(val);
   } else {
@@ -245,9 +245,9 @@ static void c25_att_combobox_changed(GtkWidget *widget, gpointer data) {
 static void c25_preamp_combobox_changed(GtkWidget *widget, gpointer data) {
   int val = atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)));
   if (active_receiver->id == 0) {
-    //DL1YCF: This button is only valid for the first receiver
-    //        dither and preamp are "misused" to store the PreAmp value.
-    //        this has to be exploited in meter.c
+    // This button is only valid for the first receiver
+    // dither and preamp are "misused" to store the PreAmp value.
+    // this has to be exploited in meter.c
     active_receiver->dither = (val >= 2);  // second preamp ON
     active_receiver->preamp = (val >= 1);  // first  preamp ON
   } else{
diff --git a/transmitter.c b/transmitter.c
index 465ec9f..00d5e4e 100644
--- a/transmitter.c
+++ b/transmitter.c
@@ -173,6 +173,9 @@ void transmitter_save_state(TRANSMITTER *tx) {
   sprintf(name,"transmitter.%d.feedback",tx->id);
   sprintf(value,"%d",tx->feedback);
   setProperty(name,value);
+  sprintf(name,"transmitter.%d.attenuation",tx->id);
+  sprintf(value,"%d",tx->attenuation);
+  setProperty(name,value);
 #endif
   sprintf(name,"transmitter.%d.ctcss",tx->id);
   sprintf(value,"%d",tx->ctcss);
@@ -259,6 +262,9 @@ void transmitter_restore_state(TRANSMITTER *tx) {
   sprintf(name,"transmitter.%d.feedback",tx->id);
   value=getProperty(name);
   if(value) tx->feedback=atoi(value);
+  sprintf(name,"transmitter.%d.attenuation",tx->id);
+  value=getProperty(name);
+  if(value) tx->attenuation=atoi(value);
 #endif
   sprintf(name,"transmitter.%d.ctcss",tx->id);
   value=getProperty(name);
@@ -1087,19 +1093,23 @@ void tx_set_displaying(TRANSMITTER *tx,int state) {
 
 #ifdef PURESIGNAL
 void tx_set_ps(TRANSMITTER *tx,int state) {
-  tx->puresignal=state;
   if(state) {
+    tx->puresignal=1;
     SetPSControl(tx->id, 0, 0, 1, 0);
   } else {
     SetPSControl(tx->id, 1, 0, 0, 0);
+    // wait a moment for PS to shut down
+    usleep(100000);
+    tx->puresignal=0;
   }
   vfo_update();
 }
 
 void tx_set_twotone(TRANSMITTER *tx,int state) {
+  fprintf(stderr,"TX TWO TONE new state=%d\n", state);
   transmitter->twotone=state;
   if(state) {
-    // DL1YCF: set frequencies and levels
+    // set frequencies and levels
     switch(tx->mode) {
       case modeCWL:
       case modeLSB:
@@ -1130,13 +1140,13 @@ void tx_set_ps_sample_rate(TRANSMITTER *tx,int rate) {
 //
 void cw_hold_key(int state) {
   if (state) {
-    cw_key_down = 4800000;    // up to 100 sec
+    cw_key_down = 480000;    // up to 10 sec
   } else {
     cw_key_down = 0;
   }
 }
 
-// DL1YCF:
+// Sine tone generator:
 // somewhat improved, and provided two siblings
 // for generating side tones simultaneously on the
 // HPSDR board and local audio.
diff --git a/tx_menu.c b/tx_menu.c
index 7e08fc9..895bce0 100644
--- a/tx_menu.c
+++ b/tx_menu.c
@@ -59,10 +59,12 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d
 
 static void comp_enable_cb(GtkWidget *widget, gpointer data) {
   transmitter->compressor=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+  g_idle_add(ext_vfo_update, NULL);
 }
 
 static void comp_cb(GtkWidget *widget, gpointer data) {
   transmitter_set_compressor_level(transmitter,gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
+  g_idle_add(ext_vfo_update, NULL);
 }
 
 static void tx_spin_low_cb (GtkWidget *widget, gpointer data) {
@@ -160,9 +162,6 @@ static void local_input_changed_cb(GtkWidget *widget, gpointer data) {
 static gboolean emp_cb (GtkWidget *widget, gpointer data) {
   pre_emphasize=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
   tx_set_pre_emphasize(transmitter,pre_emphasize);
-  // DL1YCF added return statement to make the compiler happy.
-  // however I am unsure about the correct return value.
-  // I would have coded this as a void function.
   return FALSE;
 }
 
diff --git a/tx_panadapter.c b/tx_panadapter.c
index 7a8d590..e9690f7 100644
--- a/tx_panadapter.c
+++ b/tx_panadapter.c
@@ -164,9 +164,6 @@ tx_panadapter_scroll_event_cb (GtkWidget      *widget,
   } else {
     vfo_move(-step);
   }
-  // DL1YCF added return statement to make the compiler happy.
-  // however I am unsure about the correct return value.
-  // I would have coded this as a void function.
   return FALSE;
 }
 
diff --git a/vfo.c b/vfo.c
index 20ead0c..997b4ec 100644
--- a/vfo.c
+++ b/vfo.c
@@ -687,7 +687,7 @@ void vfo_update() {
         }
         cairo_show_text(cr, temp_text);
 
-	// DL1YCF: in what follows, we want to display the VFO frequency
+	// In what follows, we want to display the VFO frequency
 	// on which we currently transmit a signal with red colour.
 	// If it is out-of-band, we display "Out of band" in red.
         // Frequencies we are not transmitting on are displayed in green
@@ -742,39 +742,34 @@ void vfo_update() {
         cairo_set_font_size(cr, 12);
         cairo_show_text(cr, temp_text);
 
+	// NB and NB2 are mutually exclusive, therefore
+	// they are put to the same place
         cairo_move_to(cr, 150, 50);
         if(active_receiver->nb) {
           cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
-        } else {
-          cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
-        }
-        cairo_show_text(cr, "NB");
-
-        cairo_move_to(cr, 175, 50);
-        if(active_receiver->nb2) {
+          cairo_show_text(cr, "NB");
+        } else if (active_receiver->nb2) {
           cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
-        } else {
+          cairo_show_text(cr, "NB2");
+	} else {
           cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
+          cairo_show_text(cr, "NB");
         }
-        cairo_show_text(cr, "NB2");
 
-        cairo_move_to(cr, 200, 50);  
+	// NR and NR2 are mutually exclusive
+        cairo_move_to(cr, 180, 50);  
         if(active_receiver->nr) {
           cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
-        } else {
-          cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
-        }
-        cairo_show_text(cr, "NR");
-
-        cairo_move_to(cr, 225, 50);  
-        if(active_receiver->nr2) {
+          cairo_show_text(cr, "NR");
+        } else if (active_receiver->nr2) {
           cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
-        } else {
+          cairo_show_text(cr, "NR2");
+	} else {
           cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
+          cairo_show_text(cr, "NR2");
         }
-        cairo_show_text(cr, "NR2");
 
-        cairo_move_to(cr, 250, 50);  
+        cairo_move_to(cr, 210, 50);  
         if(active_receiver->anf) {
           cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
         } else {
@@ -782,7 +777,7 @@ void vfo_update() {
         }
         cairo_show_text(cr, "ANF");
 
-        cairo_move_to(cr, 275, 50);  
+        cairo_move_to(cr, 240, 50);  
         if(active_receiver->snb) {
           cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
         } else {
@@ -790,7 +785,7 @@ void vfo_update() {
         }
         cairo_show_text(cr, "SNB");
 
-        cairo_move_to(cr, 350, 50);  
+        cairo_move_to(cr, 300, 50);  
         switch(active_receiver->agc) {
           case AGC_OFF:
             cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
@@ -806,7 +801,7 @@ void vfo_update() {
             break;
           case AGC_MEDIUM:
             cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
-            cairo_show_text(cr, "AGC MEDIUM");
+            cairo_show_text(cr, "AGC MED");
             break;
           case AGC_FAST:
             cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
@@ -814,6 +809,20 @@ void vfo_update() {
             break;
         }
 
+	//
+	// Since we can now change it by a MIDI controller,
+	// we should display the compressor (level)
+	//
+        cairo_move_to(cr, 400, 50);  
+	if (transmitter->compressor) {
+	    sprintf(temp_text,"CMPR %d dB",(int) transmitter->compressor_level);
+            cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
+            cairo_show_text(cr, temp_text);
+	} else {
+            cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
+            cairo_show_text(cr, "CMPR OFF");
+	}
+
         int s=0;
         while(steps[s]!=step && steps[s]!=0) {
           s++;
diff --git a/vox_menu.c b/vox_menu.c
index e14d5f2..ff418a5 100644
--- a/vox_menu.c
+++ b/vox_menu.c
@@ -91,7 +91,6 @@ static gpointer level_thread(gpointer arg) {
     g_idle_add(level_update,NULL);
     usleep(100000); // 100ms
   }
-  // DL1YCF added return statement to make compilers happy.
   return NULL;
 }