From: c vw <dl1ycf@darc.de>
Date: Wed, 15 May 2019 08:26:24 +0000 (+0200)
Subject: Intermediat commit to make it visible to someone asking for this
X-Git-Url: https://git.rkrishnan.org/pf/components/(%5B%5E?a=commitdiff_plain;h=a2054e50167b9b34ae4d4febadd7cb73dd555b57;p=pihpsdr.git

Intermediat commit to make it visible to someone asking for this
---

diff --git a/discovered.h b/discovered.h
index 7e66b5b..7c14ec0 100644
--- a/discovered.h
+++ b/discovered.h
@@ -34,7 +34,7 @@
 #define DEVICE_ANGELIA 4
 #define DEVICE_ORION 5
 #define DEVICE_HERMES_LITE 6
-// 8000DLE uses 10 as the device type in old protocol
+// ANAN 7000DLE and 8000DLE uses 10 as the device type in old protocol
 #define DEVICE_ORION2 10 
 // Newer STEMlab hpsdr emulators use 100 instead of 1
 #define DEVICE_STEMLAB 100
diff --git a/mac_midi.c b/mac_midi.c
index ad9885b..06c84ca 100644
--- a/mac_midi.c
+++ b/mac_midi.c
@@ -5,13 +5,21 @@
  * 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.
+ * which is called with the name of a supported MIDI device.
+ * This name may be truncated:  For example, the call
+ * register_midi_device("COMPANY MIDI")
+ * will accept both "COMPANY MIDI X" and "COMPANY MIDI Y" devices.
  *
- * For example, the call
- * register_midi_device("COMPANY MIDI X",13)
- * will also use a MIDI device named "COMPANY MIDI Y".
+ * If more than one MIDI device matches the name, the LAST ONE
+ * found will be taken. This may not be predictable, so it is
+ * better to say that one of the matching MIDI devices will be taken.
+ * It is easy to change the code such that ALL devices matching the
+ * name will be taken. But who cares? Normally there will only be a
+ * single MIDI controller connected to the computer running the SDR
+ * program.
+ *
+ * The name is actually specified by the user in the midi.inp file
+ * (see midi2.c)
  *
  * This file must generate calls to Layer-2 NewMidiEvent().
  * Some type of messages are not consideres (pressure change, etc.),
@@ -23,6 +31,12 @@
 
 #ifdef __APPLE__
 
+/*
+ * For MacOS, things are easy:
+ * The OS takes care of everything, we only have to register a callback
+ * routine.
+ */
+
 #include <Carbon/Carbon.h>
 
 #include <CoreMIDI/MIDIServices.h>
@@ -31,6 +45,9 @@
 
 //
 // MIDI callback function
+// called by MacOSX when data from the specified MIDI device arrives.
+// We process *all* data but only generate calls to layer-2 for Note On/Off
+// and ControllerChange events.
 //
 static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {
     int i,j,k,command,chan;
@@ -49,12 +66,12 @@ static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *co
                 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]);
+			//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]);
+			//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.
@@ -63,7 +80,7 @@ static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *co
 			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]);
+			//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
@@ -76,7 +93,7 @@ static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *co
 			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);
+			//fprintf(stderr,"Pitch   : val =%d Chan=%d\n", packet->data[i+1] + 128*packet->data[i+2], chan);
 			i +=3;
 			break;
                   case 0xF0:  
@@ -120,7 +137,6 @@ void register_midi_device(char *myname) {
 		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);
 	    }
 	}
diff --git a/midi2.c b/midi2.c
index 1e737c1..377a86e 100644
--- a/midi2.c
+++ b/midi2.c
@@ -67,7 +67,7 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
 			DoTheMidi(desc->action, desc->type, new);
 		    }
 		    break;
-		default:
+		case EVENT_NONE:
 		    break;
 	    }
 	    break;
diff --git a/midi3.c b/midi3.c
index f3989d0..8e0c33f 100644
--- a/midi3.c
+++ b/midi3.c
@@ -226,7 +226,8 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
 		    break;
 	    }
 	    break;
-	default:
+	case COMPRESS:
+	case ACTION_NONE:
             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 235ca12..3704bb5 100644
--- a/old_discovery.c
+++ b/old_discovery.c
@@ -32,6 +32,7 @@
 #include <string.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <sys/select.h>
 
 #include "discovered.h"
 #include "discovery.h"
@@ -56,6 +57,11 @@ static void discover(struct ifaddrs* iface) {
     struct sockaddr_in to_addr={0};
     int flags;
     struct timeval tv;
+    int optval;
+    socklen_t optlen;
+    fd_set fds;
+    unsigned char buffer[1032];
+    int i, len;
 
     if (iface == NULL) {
 	//
@@ -80,20 +86,59 @@ static void discover(struct ifaddrs* iface) {
             return;
 	}
 	//
-	// We make a time-out of 3 secs, otherwise we might "hang" in connect()
+	// Here I tried a bullet-proof approach to connect() such that the program
+        // does not "hang" under any circumstances.
+	// - First, one makes the socket non-blocking. Then, the connect() will
+        //   immediately return with error EINPROGRESS.
+	// - Then, one uses select() to look for *writeability* and check
+	//   the socket error if everything went right. Since one calls select()
+        //   with a time-out, one either succeed within this time or gives up.
+        // - Do not forget to make the socket blocking again.
 	//
+        // Step 1. Make socket non-blocking and connect()
 	flags=fcntl(discovery_socket, F_GETFL, 0);
 	fcntl(discovery_socket, F_SETFL, flags | O_NONBLOCK);
+	rc=connect(discovery_socket, (const struct sockaddr *)&to_addr, sizeof(to_addr));
+        if ((errno != EINPROGRESS) && (rc < 0)) {
+            perror("discover: connect() failed for TCP discovery_socket:");
+	    close(discovery_socket);
+	    return;
+	}
+	// Step 2. Use select to wait for the connection
         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:");
+	FD_ZERO(&fds);
+	FD_SET(discovery_socket, &fds);
+	rc=select(discovery_socket+1, NULL, &fds, NULL, &tv);
+        if (rc < 0) {
+            perror("discover: select() failed on TCP discovery_socket:");
+	    close(discovery_socket);
+	    return;
+        }
+	// If no connection occured, return
+	if (rc == 0) {
+	    // select timed out
+	    fprintf(stderr,"discover: select() timed out on TCP discovery socket\n");
 	    close(discovery_socket);
 	    return;
 	}
+	// Step 3. select() succeeded. Check success of connect()
+	optlen=sizeof(int);
+	rc=getsockopt(discovery_socket, SOL_SOCKET, SO_ERROR, &optval, &optlen);
+	if (rc < 0) {
+	    // this should very rarely happen
+            perror("discover: getsockopt() failed on TCP discovery_socket:");
+	    close(discovery_socket);
+	    return;
+	}
+	if (optval != 0) {
+	    // connect did not succeed
+	    fprintf(stderr,"discover: connect() on TCP socket did not succeed\n");
+	    close(discovery_socket);
+	    return;
+	}
+	// Step 4. reset the socket to normal (blocking) mode
+	fcntl(discovery_socket, F_SETFL, flags &  ~O_NONBLOCK);
     } else {
 
         strcpy(interface_name,iface->ifa_name);
@@ -135,7 +180,7 @@ static void discover(struct ifaddrs* iface) {
         to_addr.sin_port=htons(DISCOVERY_PORT);
         to_addr.sin_addr.s_addr=htonl(INADDR_BROADCAST);
     }
-    int optval = 1;
+    optval = 1;
     setsockopt(discovery_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
     setsockopt(discovery_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
 
@@ -152,13 +197,11 @@ static void discover(struct ifaddrs* iface) {
 
     // send discovery packet
     // If this is a TCP connection, send a "long" packet
-    unsigned char buffer[1032];
-    int len=63;
+    len=63;
     if (iface == NULL) len=1032;
     buffer[0]=0xEF;
     buffer[1]=0xFE;
     buffer[2]=0x02;
-    int i;
     for(i=3;i<len;i++) {
         buffer[i]=0x00;
     }
diff --git a/old_protocol.c b/old_protocol.c
index 4a5a6fb..c12a7b1 100644
--- a/old_protocol.c
+++ b/old_protocol.c
@@ -97,10 +97,6 @@
 #define SPEED_384K                0x03
 #define MODE_CLASS_E              0x01
 #define MODE_OTHERS               0x00
-#define ALEX_ATTENUATION_0DB      0x00
-#define ALEX_ATTENUATION_10DB     0x01
-#define ALEX_ATTENUATION_20DB     0x02
-#define ALEX_ATTENUATION_30DB     0x03
 #define LT2208_GAIN_OFF           0x00
 #define LT2208_GAIN_ON            0x04
 #define LT2208_DITHER_OFF         0x00
@@ -627,7 +623,13 @@ static void process_ozy_input_buffer(unsigned char  *buffer) {
 
 
 #ifdef PURESIGNAL
-    nreceivers=(RECEIVERS*2)+1;
+    // 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.
+    nreceivers=3;
+    if (device == DEVICE_HERMES) nreceivers=4;
+    if (device == DEVICE_ANGELIA || device == DEVICE_ORION || device == DEVICE_ORION2) nreceivers=5;
 #else
 	#if defined(RADIOBERRY) || defined(PI_SDR)
 		nreceivers = receivers;
@@ -853,6 +855,9 @@ void ozy_send_buffer() {
   output_buffer[SYNC2]=SYNC;
 
   if(metis_offset==8) {
+    //
+    // Every second packet is a "C0=0" packet
+    //
     output_buffer[C0]=0x00;
     output_buffer[C1]=0x00;
     switch(active_receiver->sample_rate) {
@@ -912,7 +917,6 @@ void ozy_send_buffer() {
       output_buffer[C2]|=band->OCrx<<1;
     }
 
-// TODO - add Alex Antenna
     output_buffer[C3] = (receiver[0]->alex_attenuation) & 0x03;  // do not set higher bits
     if(active_receiver->random) {
       output_buffer[C3]|=LT2208_RANDOM_ON;
@@ -933,19 +937,25 @@ void ozy_send_buffer() {
       output_buffer[C3]|=LT2208_GAIN_ON;
     }
 
-    switch(receiver[0]->alex_antenna) {
+    i=receiver[0]->alex_antenna;
+#ifdef PURESIGNAL
+    //
+    // 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 (e.g.
+    // ANAN-7000DLE, when using the internal feedback path).
+    //
+    if (isTransmitting() && transmitter->puresignal) i=receiver[PS_RX_FEEDBACK]->feedback_antenna;
+#endif
+    switch(i) {
       case 0:  // ANT 1
-        break;
       case 1:  // ANT 2
-        break;
       case 2:  // ANT 3
         break;
-      case 3:  // EXT 1
-        //output_buffer[C3]|=0xA0;
+      case 3:  // Alex: RX2 IN, ANAN: EXT1, ANAN7000: EXT
         output_buffer[C3]|=0xC0;
         break;
-      case 4:  // EXT 2
-        //output_buffer[C3]|=0xC0;
+      case 4:  // Alex: RX1 IN, ANAN: EXT2, ANAN7000: RX BYPASS
         output_buffer[C3]|=0xA0;
         break;
       case 5:  // XVTR
@@ -959,8 +969,10 @@ void ozy_send_buffer() {
 // TODO - add Alex TX relay, duplex, receivers Mercury board frequency
     output_buffer[C4]=0x04;  // duplex
 #ifdef PURESIGNAL
-    nreceivers=(RECEIVERS*2)-1;
-    nreceivers+=1; // for PS TX Feedback
+    // DL1YCF: see comment on "nreceivers" above. The number is reduced by 1 here
+    nreceivers=2;
+    if (device == DEVICE_HERMES) nreceivers=3;
+    if (device == DEVICE_ANGELIA || device == DEVICE_ORION || device == DEVICE_ORION2) nreceivers=4;
 #else
 	#ifdef RADIOBERRY
 		nreceivers = receivers-1;
@@ -969,6 +981,7 @@ void ozy_send_buffer() {
 	#endif
 #endif
 
+    // 0 ... 7 maps on 1 ... 8 receivers
     output_buffer[C4]|=nreceivers<<3;
     
     if(isTransmitting()) {
@@ -1014,6 +1027,10 @@ void ozy_send_buffer() {
       }
     }
   } else {
+    //
+    // metis_offset !=8: send "command" C&C packets in round-robin
+    // using the value of "command" from 1 to 10,
+    // and "command==2" packets are repeated for each RX
     switch(command) {
       case 1: // tx frequency
         output_buffer[C0]=0x02;
@@ -1038,7 +1055,10 @@ void ozy_send_buffer() {
         break;
       case 2: // rx frequency
 #ifdef PURESIGNAL
-        nreceivers=(RECEIVERS*2)+1;
+	// DL1YCF: see comment on "nreceivers" above.
+	nreceivers=3;
+	if (device == DEVICE_HERMES) nreceivers=4;
+	if (device == DEVICE_ANGELIA || device == DEVICE_ORION || device == DEVICE_ORION2) nreceivers=5;
 #else
 		#ifdef RADIOBERRY
 			nreceivers = receivers;
@@ -1175,28 +1195,30 @@ void ozy_send_buffer() {
         }
 #endif
         output_buffer[C3]=0x00;
+        output_buffer[C4]=0x00;
   
         if(radio->device==DEVICE_HERMES || radio->device==DEVICE_ANGELIA || radio->device==DEVICE_ORION || radio->device==DEVICE_ORION2) {
-          output_buffer[C4]=0x20|adc_attenuation[receiver[0]->adc];
+	  // DL1YCF: if attenuation is zero, then disable attenuator
+	  i = adc_attenuation[receiver[0]->adc] & 0x1F;
+          if (i >0) output_buffer[C4]=0x20| i;
         } else {
 #ifdef RADIOBERRY
-		  int att = 63 - rx_gain_slider[active_receiver->adc];
+	  int att = 63 - rx_gain_slider[active_receiver->adc];
           output_buffer[C4]=0x20|att;
-#else
-		  output_buffer[C4]=0x00;
 #endif
         }
         break;
       case 5:
-        // need to add adc 2 and 3 attenuation
         output_buffer[C0]=0x16;
         output_buffer[C1]=0x00;
         if(receivers==2) {
           if(radio->device==DEVICE_HERMES || radio->device==DEVICE_ANGELIA || radio->device==DEVICE_ORION || radio->device==DEVICE_ORION2) {
-            output_buffer[C1]=0x20|adc_attenuation[receiver[1]->adc];
+	    // DL1YCF: if attenuation is zero, then disable attenuator
+	    i = adc_attenuation[receiver[1]->adc] & 0x1F;
+            if (i > 0) output_buffer[C1]=0x20|i;
           }
         }
-        output_buffer[C2]=0x00;
+        output_buffer[C2]=0x00; // ADC3 attenuator disabled.
         if(cw_keys_reversed!=0) {
           output_buffer[C2]|=0x40;
         }
@@ -1208,12 +1230,30 @@ void ozy_send_buffer() {
         output_buffer[C0]=0x1C;
         output_buffer[C1]=0x00;
 #ifdef PURESIGNAL
-        output_buffer[C1]|=receiver[0]->adc;
-        output_buffer[C1]|=(receiver[0]->adc<<2);
-        output_buffer[C1]|=receiver[1]->adc<<4;
-        output_buffer[C1]|=(receiver[1]->adc<<6);
+
+	// 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?
+	}
         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
@@ -1263,14 +1303,14 @@ void ozy_send_buffer() {
         output_buffer[C0]=0x24;
         output_buffer[C1]=0x00;
         if(isTransmitting()) {
-          output_buffer[C1]|=0x80; // ground RX1 on transmit
+          output_buffer[C1]|=0x80; // ground RX2 on transmit, bit0-6 are Alex2 filters
         }
         output_buffer[C2]=0x00;
         if(receiver[0]->alex_antenna==5) { // XVTR
-          output_buffer[C2]=0x02;
+          output_buffer[C2]=0x02;          // Alex2 XVTR enable
         }
-        output_buffer[C3]=0x00;
-        output_buffer[C4]=0x00;
+        output_buffer[C3]=0x00;            // Alex2 filters
+        output_buffer[C4]=0x00;            // Alex2 filters
         break;
     }
 
diff --git a/ps_menu.c b/ps_menu.c
index d6d5924..9312459 100644
--- a/ps_menu.c
+++ b/ps_menu.c
@@ -227,11 +227,12 @@ static int info_thread(gpointer arg) {
       switch(state) {
         case 0:
           if(newcal && (info[4]>181 || (info[4]<=128 && transmitter->attenuation>0))) {
-            ddb= 20.0 * log10((double)info[4]/152.293);
-            if(isnan(ddb)) {
-		// this means feedback lvl is < 1, switch OFF attenuation
-                ddb=-100.0;
-            }
+	    if (info[4] > 0) {
+              ddb= 20.0 * log10((double)info[4]/152.293);
+	    } else {
+	      // This happens when the "Drive" slider is moved to zero
+	      ddb= -100.0;
+	    }
             new_att=transmitter->attenuation + (int)ddb;
 	    // keep new value of attenuation in allowed range
 	    if (new_att <  0) new_att= 0;
@@ -263,6 +264,12 @@ static int info_thread(gpointer arg) {
     return TRUE;
 }
 
+static void ps_ant_cb(GtkWidget *widget, gpointer data) {
+  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+    receiver[PS_RX_FEEDBACK]->feedback_antenna = (int) (uintptr_t) data;
+  }
+}
+
 static void enable_cb(GtkWidget *widget, gpointer data) {
   g_idle_add(ext_tx_set_ps,(gpointer)(long)gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
 }
@@ -358,7 +365,6 @@ void ps_menu(GtkWidget *parent) {
     set_button_text_color(twotone_b,"red");
   }
 
-  
   col++;
 
   GtkWidget *auto_b=gtk_check_button_new_with_label("Auto Attenuate");
@@ -392,6 +398,30 @@ void ps_menu(GtkWidget *parent) {
   row++;
   col=0;
 
+  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);
+  col++;
+
+  GtkWidget *ps_ant_auto=gtk_radio_button_new_with_label(NULL,"AUTO");
+  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_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_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);
+
+  row++;
+  col=0;
   feedback_l=gtk_label_new("Feedback Lvl");
   gtk_widget_show(feedback_l);
   gtk_grid_attach(GTK_GRID(grid),feedback_l,col,row,1,1);
diff --git a/radio.c b/radio.c
index b47623e..b4ccf88 100644
--- a/radio.c
+++ b/radio.c
@@ -352,7 +352,7 @@ static gboolean minimize_cb (GtkWidget *widget, GdkEventButton *event, gpointer
 }
 
 static gboolean menu_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
-  new_menu(top_window);
+  new_menu();
   return TRUE;
 }
 
@@ -1014,6 +1014,21 @@ static int calcLevel(double d) {
 
   level=(int)(actual_volts*255.0);
 
+#ifdef __APPLE__
+#ifdef PURESIGNAL
+//
+//  DL1YCF: I do not know exactly why: if the drive level
+//          is set to zero while PS is active, the program
+//          reproducably crashes when the drive is set from 1 Watt
+//          to 0 Watt, possibly a division by zero or the evaluation
+//          of a logarithm within WDSP.
+//          QuickAndDirty Fix: use "1" as minimum drive level
+//          which corresponds to a fraction of a Watt.
+//
+  if (level < 1) level=1;
+#endif
+#endif
+ 
 //fprintf(stderr,"calcLevel: %f calib=%f level=%d\n",d, gbb, level);
   return level;
 }
@@ -1482,6 +1497,11 @@ void radioSaveState() {
     for(i=0;i<receivers;i++) {
       receiver_save_state(receiver[i]);
     }
+#ifdef PURESIGNAL
+    // There is little to save.
+    // An exception is the feedback_antenna stored in PS_RX_FEEDBACK
+    receiver_save_state(receiver[PS_RX_FEEDBACK]);
+#endif
     transmitter_save_state(transmitter);
 #ifdef FREEDV
     freedv_save_state();
diff --git a/receiver.c b/receiver.c
index 2817602..bdaf835 100644
--- a/receiver.c
+++ b/receiver.c
@@ -125,14 +125,15 @@ gboolean receiver_motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
   int x, y;
   GdkModifierType state;
   RECEIVER *rx=(RECEIVER *)data;
-  if(!making_active) {
+  // DL1YCF: if !pressed, we may come from the destruction
+  //         of a menu, and should not move the VFO.
+  if(!making_active && pressed) {
     gdk_window_get_device_position (event->window,
                                 event->device,
                                 &x,
                                 &y,
                                 &state);
-    // DL1YCF added a pair of () to fix an error
-    if(((state & GDK_BUTTON1_MASK) == GDK_BUTTON1_MASK) || pressed) {
+    if(state & GDK_BUTTON1_MASK) {
       int moved=last_x-x;
       vfo_move((long long)((float)moved*rx->hz_per_pixel));
       last_x=x;
@@ -198,6 +199,11 @@ void receiver_save_state(RECEIVER *rx) {
   sprintf(value,"%d",rx->waterfall_automatic);
   setProperty(name,value);
   
+#ifdef PURESIGNAL
+  sprintf(name,"receiver.%d.feedback_antenna",rx->id);
+  sprintf(value,"%d",rx->feedback_antenna);
+  setProperty(name,value);
+#endif
   sprintf(name,"receiver.%d.alex_antenna",rx->id);
   sprintf(value,"%d",rx->alex_antenna);
   setProperty(name,value);
@@ -306,15 +312,9 @@ fprintf(stderr,"receiver_restore_state: id=%d\n",rx->id);
   sprintf(name,"receiver.%d.sample_rate",rx->id);
   value=getProperty(name);
   if(value) rx->sample_rate=atoi(value);
-#ifdef STEMLAB_FIX
-  // HPSDR apps on the RedPitay have hard-wired connections
-  // that should not be changed
-  fprintf(stderr,"STEMLAB: ignoring ADC settings for RX%d\n",rx->id);
-#else
   sprintf(name,"receiver.%d.adc",rx->id);
   value=getProperty(name);
   if(value) rx->adc=atoi(value);
-#endif
   sprintf(name,"receiver.%d.filter_low",rx->id);
   value=getProperty(name);
   if(value) rx->filter_low=atoi(value);
@@ -360,6 +360,11 @@ fprintf(stderr,"receiver_restore_state: id=%d\n",rx->id);
   value=getProperty(name);
   if(value) rx->waterfall_automatic=atoi(value);
 
+#ifdef PURESIGNAL
+  sprintf(name,"receiver.%d.feedback_antenna",rx->id);
+  value=getProperty(name);
+  if(value) rx->feedback_antenna=atoi(value);
+#endif
   sprintf(name,"receiver.%d.alex_antenna",rx->id);
   value=getProperty(name);
   if(value) rx->alex_antenna=atoi(value);
@@ -774,6 +779,9 @@ fprintf(stderr,"create_pure_signal_receiver: id=%d buffer_size=%d\n",id,buffer_s
 
   rx->volume=0.0;
 
+  rx->squelch_enable=0;
+  rx->squelch=0;
+
   rx->dither=0;
   rx->random=0;
   rx->preamp=0;
@@ -790,6 +798,7 @@ fprintf(stderr,"create_pure_signal_receiver: id=%d buffer_size=%d\n",id,buffer_s
   rx->nr2_npe_method=0;
   rx->nr2_ae=1;
   
+  rx->feedback_antenna=0;
   rx->alex_antenna=0;
   rx->alex_attenuation=0;
 
@@ -808,6 +817,9 @@ fprintf(stderr,"create_pure_signal_receiver: id=%d buffer_size=%d\n",id,buffer_s
 
   rx->low_latency=0;
 
+  // not much to be restored, except feedback_antenna
+  if (id == PS_RX_FEEDBACK) receiver_restore_state(rx);
+
   int result;
   XCreateAnalyzer(rx->id, &result, 262144, 1, 1, "");
   if(result != 0) {
@@ -867,12 +879,6 @@ fprintf(stderr,"create_receiver: id=%d buffer_size=%d fft_size=%d pixels=%d fps=
           }
           break;
       }
-#ifdef STEMLAB_FIX
-//
-//    RedPitaya based HPSDR apps have hard-wired adc settings
-//
-      if (id == 1) rx->adc=1;
-#endif
   }
 fprintf(stderr,"create_receiver: id=%d default ddc=%d adc=%d\n",rx->id, rx->ddc, rx->adc);
   rx->sample_rate=48000;
@@ -925,6 +931,10 @@ fprintf(stderr,"create_receiver: id=%d default ddc=%d adc=%d\n",rx->id, rx->ddc,
   rx->nr2_npe_method=0;
   rx->nr2_ae=1;
   
+#ifdef PURESIGNAL
+  rx->feedback_antenna=0;
+#endif
+
   BAND *b=band_get_band(vfo[rx->id].band);
   rx->alex_antenna=b->alexRxAntenna;
   rx->alex_attenuation=b->alexAttenuation;
diff --git a/receiver.h b/receiver.h
index 39bf084..52970ca 100644
--- a/receiver.h
+++ b/receiver.h
@@ -89,6 +89,13 @@ typedef struct _receiver {
   int alex_antenna;
   int alex_attenuation;
 
+#ifdef PURESIGNAL
+  // indicates to which ALEX RX antenna the attenuated feedback
+  // signal from the PA goes. The coding is the same as for alex_antenna,
+  // except that this connector is only used when transmitting.
+  int feedback_antenna;
+#endif
+
   int filter_low;
   int filter_high;
 
diff --git a/rx_menu.c b/rx_menu.c
index 528284a..2445bdf 100644
--- a/rx_menu.c
+++ b/rx_menu.c
@@ -59,7 +59,6 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d
 
 static void dither_cb(GtkWidget *widget, gpointer data) {
   active_receiver->dither=active_receiver->dither==1?0:1;
-  update_att_preamp();
 }
 
 static void random_cb(GtkWidget *widget, gpointer data) {
@@ -68,7 +67,6 @@ static void random_cb(GtkWidget *widget, gpointer data) {
 
 static void preamp_cb(GtkWidget *widget, gpointer data) {
   active_receiver->preamp=active_receiver->preamp==1?0:1;
-  update_att_preamp();
 }
 
 static void alex_att_cb(GtkWidget *widget, gpointer data) {
@@ -238,9 +236,17 @@ void rx_menu(GtkWidget *parent) {
       break;
 #endif
   }
-
+ 
+  //
   // The CHARLY25 board (with RedPitaya) has no support for dither or random,
-  // so those are left out. PreAmps and Alex Attenuator are controlled via sliders.
+  // so those are left out. For Charly25, PreAmps and Alex Attenuator are controlled via
+  // the sliders menu.
+  // On SDRs other than CHARLY25, preamps or Alex attenuators may be present or not, and we
+  // do not try to find out whether they are. This would overload the code, and we then
+  // also must have a menu to check e.g. which ANAN model is actually present.
+  // Instead, we offer these checkboxes in either case and must rely on the user
+  // not playing around with features that are not there.
+  //
   if (filter_board != CHARLY25) {
     switch(protocol) {
       case ORIGINAL_PROTOCOL:
@@ -256,17 +262,11 @@ void rx_menu(GtkWidget *parent) {
           gtk_grid_attach(GTK_GRID(grid),random_b,x,3,1,1);
           g_signal_connect(random_b,"toggled",G_CALLBACK(random_cb),NULL);
 
-          if((protocol==ORIGINAL_PROTOCOL && device==DEVICE_METIS) ||
-#ifdef USBOZY
-              (protocol==ORIGINAL_PROTOCOL && device==DEVICE_OZY) ||
-#endif
-              (protocol==NEW_PROTOCOL && device==NEW_DEVICE_ATLAS)) {
+          GtkWidget *preamp_b=gtk_check_button_new_with_label("Preamp");
+          gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (preamp_b), active_receiver->preamp);
+          gtk_grid_attach(GTK_GRID(grid),preamp_b,x,4,1,1);
+          g_signal_connect(preamp_b,"toggled",G_CALLBACK(preamp_cb),NULL);
 
-            GtkWidget *preamp_b=gtk_check_button_new_with_label("Preamp");
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (preamp_b), active_receiver->preamp);
-            gtk_grid_attach(GTK_GRID(grid),preamp_b,x,4,1,1);
-            g_signal_connect(preamp_b,"toggled",G_CALLBACK(preamp_cb),NULL);
-          }
           GtkWidget *alex_att_label=gtk_label_new("Alex Attenuator");
           gtk_grid_attach(GTK_GRID(grid), alex_att_label, x, 5, 1, 1);
           GtkWidget *last_alex_att_b = NULL;
@@ -287,12 +287,13 @@ 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;  // FIX for multiple Mercury cards
+          n_adc=1;  // No support for multiple MERCURY cards on a single ATLAS bus.
           break;
         case DEVICE_HERMES:
         case DEVICE_HERMES_LITE:
@@ -306,7 +307,7 @@ void rx_menu(GtkWidget *parent) {
     case NEW_PROTOCOL:
       switch(device) {
         case NEW_DEVICE_ATLAS:
-          n_adc=1; // FIX for multiple Mercury cards
+          n_adc=1; // No support for multiple MERCURY cards on a single ATLAS bus.
           break;
         case NEW_DEVICE_HERMES:
         case NEW_DEVICE_HERMES2:
@@ -322,6 +323,9 @@ void rx_menu(GtkWidget *parent) {
       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.
   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 8c71cae..5193be6 100644
--- a/sliders.c
+++ b/sliders.c
@@ -202,8 +202,6 @@ void update_att_preamp(void) {
     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;
   }
 }