]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
Add functionality to switch from UDP to TCP for CHARLY25 RedPitay-based
authorc vw <dl1ycf@darc.de>
Sun, 11 Nov 2018 12:55:11 +0000 (13:55 +0100)
committerc vw <dl1ycf@darc.de>
Sun, 11 Nov 2018 12:55:11 +0000 (13:55 +0100)
SDRs. Added support for this in the RADIO menu.

old_protocol.c
radio.c
radio.h
radio_menu.c

index 99264c66bee98aa6475f6638aefa6ada092bb6b1..7803079acdd108f08e44e78c0ccab7a6fdf41578 100644 (file)
@@ -118,6 +118,7 @@ static int dsp_rate=48000;
 static int output_rate=48000;
 
 static int data_socket;
+static int tcp_socket=-1;
 static struct sockaddr_in data_addr;
 static int data_addr_length;
 
@@ -342,16 +343,26 @@ static void start_receive_thread() {
       if(setsockopt(data_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval))<0) {
         perror("data_socket: SO_REUSEPORT");
       }
-
-/*
+      optval=0xffff;
+      if (setsockopt(data_socket, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval))<0) {
+        perror("tcp_socket: SO_SNDBUF");
+      }
+      if (setsockopt(data_socket, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval))<0) {
+        perror("tcp_socket: SO_RCVBUF");
+      }
+      //
       // set a timeout for receive
+      // This is necessary because we might already "sit" in an UDP recvfrom() call while
+      // instructing the radio to switch to TCP. Then this call has to finish eventually
+      // and the next recvfrom() then uses the TCP socket.
+      //
       struct timeval tv;
       tv.tv_sec=1;
       tv.tv_usec=0;
       if(setsockopt(data_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))<0) {
         perror("data_socket: SO_RCVTIMEO");
       }
-*/
+
       // bind to the interface
       if(bind(data_socket,(struct sockaddr*)&radio->info.network.interface_address,radio->info.network.interface_length)<0) {
         perror("old_protocol: bind socket failed for data_socket\n");
@@ -379,6 +390,7 @@ static gpointer receive_thread(gpointer arg) {
   socklen_t length;
   unsigned char buffer[2048];
   int bytes_read;
+  int ret,left;
   int ep;
   long sequence;
 
@@ -396,13 +408,27 @@ static gpointer receive_thread(gpointer arg) {
 #endif
 
       default:
-        bytes_read=recvfrom(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&addr,&length);
-        if(bytes_read<0) {
-          if(errno==EAGAIN) {
-            error_handler("old_protocol: receiver_thread: recvfrom socket failed","Radio not sending data");
-          } else {
-            error_handler("old_protocol: receiver_thread: recvfrom socket failed",strerror(errno));
-          }
+       for (;;) {
+          if (tcp_socket >= 0) {
+           // TCP messages may be split, so collect exactly 1032 bytes.
+           // Remember, this is a STREAMING protocol.
+            bytes_read=0;
+            left=1032;
+            while ((ret=recvfrom(tcp_socket,buffer+bytes_read,(size_t)(left),0,NULL,0))) {
+              if (ret < 0 && errno == EAGAIN) continue; // time-out
+              if (ret < 0) break;                       // error
+              bytes_read += ret;
+              left -= ret;
+              if (left <= 0) break;
+            }
+           if (ret < 0) bytes_read=ret;                // error case: discard whole packet
+         } else {
+            bytes_read=recvfrom(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&addr,&length);
+          } 
+          if(bytes_read >= 0 || errno != EAGAIN) break;
+       }
+        if(bytes_read < 0) {
+          error_handler("old_protocol: receiver_thread: recvfrom socket failed",strerror(errno));
           running=0;
           continue;
         }
@@ -416,7 +442,9 @@ static gpointer receive_thread(gpointer arg) {
               // get the sequence number
               sequence=((buffer[4]&0xFF)<<24)+((buffer[5]&0xFF)<<16)+((buffer[6]&0xFF)<<8)+(buffer[7]&0xFF);
 
-              if (sequence != last_seq_num+1) {
+             // A sequence error with a seqnum of zero usually indicates a METIS restart
+             // and is no error condition
+              if (sequence != 0 && sequence != last_seq_num+1) {
                fprintf(stderr,"SEQ ERROR: last %ld, recvd %ld\n", last_seq_num, sequence);
              }
              last_seq_num=sequence;
@@ -1399,11 +1427,16 @@ static void metis_restart() {
   sleep(1);
 
   // start the data flowing
-  metis_start_stop(1);
+  if (use_tcp && filter_board == CHARLY25) {
+    metis_start_stop(0x11);
+  } else {
+    metis_start_stop(1);
+  }
 }
 
 static void metis_start_stop(int command) {
   int i;
+  int tmp;
   unsigned char buffer[64];
     
 #ifdef USBOZY
@@ -1421,14 +1454,72 @@ static void metis_start_stop(int command) {
   }
 
   metis_send_buffer(buffer,sizeof(buffer));
+
+  //
+  // If the "start" command reads 0x11 instead of 1, and no TCP socket has
+  // been connected so far, then connect to a TCP socket with the same port
+  // number and address. Some Red-Pitaya based HPSDR emulators offer a
+  // TCP-based protocol this way. Note one cannot go back to UDP unless one
+  // restart the HPSDR app on the RedPitaya.
+  //
+  // Note that the variable tcp_socket must be set LATER to the value of
+  // the socket, such that the receive thread does not try to use this socket
+  // before it is fully functional.
+  //
+  // All subsequent METIS_SEND commands also use the TCP socket if it is
+  // activated.
+  //
+  // The RedPitaya HPSDR application does never switch back to UPD unless restarted,
+  // therefore there is no possibility to switch back.
+  //
+  tmp=tcp_socket;
+  if (command == 0x11 && tcp_socket < 0) {
+    tmp=socket(AF_INET, SOCK_STREAM, 0);
+    if (connect(tmp,(const struct sockaddr *)&data_addr,data_addr_length) < 0) {
+      perror("tcp_socket: connect");
+    }
+    int optval = 1;
+    if(setsockopt(tmp, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval))<0) {
+      perror("tcp_socket: SO_REUSEADDR");
+    }
+    if(setsockopt(tmp, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval))<0) {
+      perror("tcp_socket: SO_REUSEPORT");
+    }
+    optval=0xffff;
+    if (setsockopt(tmp, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval))<0) {
+      perror("tcp_socket: SO_SNDBUF");
+    }
+    if (setsockopt(tmp, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval))<0) {
+      perror("tcp_socket: SO_RCVBUF");
+    }
+    //
+    // set a timeout for receive
+    // This is necessary because we might already "sit" in and TCP recvfrom() call while
+    // restarting METIS with UDP
+    //
+    struct timeval tv;
+    tv.tv_sec=1;
+    tv.tv_usec=0;
+    if(setsockopt(tmp, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))<0) {
+        perror("tcp_socket: SO_RCVTIMEO");
+    }
+    tcp_socket=tmp; // this switches the receive thread to using TCP
+  }
+
 #ifdef USBOZY
   }
 #endif
 }
 
 static void metis_send_buffer(unsigned char* buffer,int length) {
-  if(sendto(data_socket,buffer,length,0,(struct sockaddr*)&data_addr,data_addr_length)!=length) {
-    perror("sendto socket failed for metis_send_data\n");
+  if (tcp_socket >= 0) {
+    if(sendto(tcp_socket,buffer,length,0,NULL, 0)!=length) {
+      perror("sendto socket failed for metis_send_data\n");
+    }
+  } else {
+    if(sendto(data_socket,buffer,length,0,(struct sockaddr*)&data_addr,data_addr_length)!=length) {
+      perror("sendto socket failed for metis_send_data\n");
+    }
   }
 }
 
diff --git a/radio.c b/radio.c
index c9bb32c00fcafcf0d8f40877951539f165a4f4ba..471f189378629ce1c07777d3ccb13bb7c5e4d7da 100644 (file)
--- a/radio.c
+++ b/radio.c
@@ -154,6 +154,7 @@ int alc=TXA_ALC_AV;
 double tone_level=0.2;
 
 int filter_board=ALEX;
+int use_tcp=0;
 //int pa=PA_ENABLED;
 //int apollo_tuner=0;
 
@@ -1103,6 +1104,8 @@ fprintf(stderr,"radioRestoreState: %s\n",property_path);
     if(value) tx_out_of_band=atoi(value);
     value=getProperty("filter_board");
     if(value) filter_board=atoi(value);
+    value=getProperty("use_tcp");
+    if(value) use_tcp=atoi(value);
 /*
     value=getProperty("apollo_tuner");
     if(value) apollo_tuner=atoi(value);
@@ -1315,6 +1318,8 @@ void radioSaveState() {
     setProperty("atlas_penelope",value);
     sprintf(value,"%d",filter_board);
     setProperty("filter_board",value);
+    sprintf(value,"%d",use_tcp);
+    setProperty("use_tcp",value);
     sprintf(value,"%d",tx_out_of_band);
     setProperty("tx_out_of_band",value);
     sprintf(value,"%d",updates_per_second);
diff --git a/radio.h b/radio.h
index 943ed24e6008bcfc80c68abac92456e86a4ebf7d..7f8d3aa5746ff86b01c4177904d16919029bd10d 100644 (file)
--- a/radio.h
+++ b/radio.h
@@ -106,6 +106,7 @@ extern int tx_leveler;
 extern double tone_level;
 
 extern int filter_board;
+extern int use_tcp;
 extern int pa;
 extern int apollo_tuner;
 
index fb915d67e1173f13850f6709a3fb0eb0f4ae39a6..7b1aef6a549d09a49cecdb90e9af48ade4515485 100644 (file)
@@ -41,6 +41,8 @@ static GtkWidget *menu_b=NULL;
 
 static GtkWidget *dialog=NULL;
 
+static GtkWidget *use_tcp_b=NULL;
+
 static void cleanup() {
   if(dialog!=NULL) {
     gtk_widget_destroy(dialog);
@@ -116,6 +118,11 @@ static void load_filters(void) {
     }
   }
   att_type_changed();
+  if (filter_board == CHARLY25) {
+    if (use_tcp_b) gtk_widget_show(use_tcp_b);
+  } else {
+    if (use_tcp_b) gtk_widget_hide(use_tcp_b);
+  }
 }
 
 static void none_cb(GtkWidget *widget, gpointer data) {
@@ -139,6 +146,20 @@ static void apollo_cb(GtkWidget *widget, gpointer data) {
   }
 }
 
+static void use_tcp_cb(GtkWidget *widget, gpointer data) {
+  if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
+    use_tcp = 1;
+    if (protocol==ORIGINAL_PROTOCOL) {
+       old_protocol_stop();
+       old_protocol_run();
+    }
+  } else {
+    // This becomes active upon next restart
+    // The current session will continue using TCP
+    use_tcp = 0;
+  }
+}
+
 static void charly25_cb(GtkWidget *widget, gpointer data) {
   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
     filter_board = CHARLY25;
@@ -347,31 +368,40 @@ void radio_menu(GtkWidget *parent) {
       x++;
     }
 
+    GtkWidget *sample_rate_label=gtk_label_new("Filter Board:");
+    gtk_grid_attach(GTK_GRID(grid),sample_rate_label,x,1,1,1);
+
     GtkWidget *none_b = gtk_radio_button_new_with_label(NULL, "NONE");
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(none_b), filter_board == NONE);
-    gtk_grid_attach(GTK_GRID(grid), none_b, x, 1, 1, 1);
+    gtk_grid_attach(GTK_GRID(grid), none_b, x, 2, 1, 1);
 
     GtkWidget *alex_b = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(none_b), "ALEX");
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(alex_b), filter_board == ALEX);
-    gtk_grid_attach(GTK_GRID(grid), alex_b, x, 2, 1, 1);
+    gtk_grid_attach(GTK_GRID(grid), alex_b, x, 3, 1, 1);
 
     GtkWidget *apollo_b = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(none_b), "APOLLO");
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(apollo_b), filter_board == APOLLO);
-    gtk_grid_attach(GTK_GRID(grid), apollo_b, x, 3, 1, 1);
+    gtk_grid_attach(GTK_GRID(grid), apollo_b, x, 4, 1, 1);
 
     GtkWidget *charly25_b = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(none_b), "CHARLY25");
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(charly25_b), filter_board == CHARLY25);
-    gtk_grid_attach(GTK_GRID(grid), charly25_b, x, 4, 1, 1);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(charly25_b), filter_board==CHARLY25);
+    gtk_grid_attach(GTK_GRID(grid), charly25_b, x, 5, 1, 1);
 
     g_signal_connect(none_b, "toggled", G_CALLBACK(none_cb), NULL);
     g_signal_connect(alex_b, "toggled", G_CALLBACK(alex_cb), NULL);
     g_signal_connect(apollo_b, "toggled", G_CALLBACK(apollo_cb), NULL);
     g_signal_connect(charly25_b, "toggled", G_CALLBACK(charly25_cb), NULL);
 
+    if (protocol == ORIGINAL_PROTOCOL) {
+      use_tcp_b = gtk_check_button_new_with_label("Use TCP not UDP");
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(use_tcp_b), use_tcp==1);
+      gtk_grid_attach(GTK_GRID(grid), use_tcp_b, x, 6, 1, 1);
+      g_signal_connect(use_tcp_b, "toggled", G_CALLBACK(use_tcp_cb), NULL);
+    }
+
     x++;
   }
 
-
   GtkWidget *rit_label=gtk_label_new("RIT step (Hz): ");
   gtk_grid_attach(GTK_GRID(grid),rit_label,x,1,1,1);
 
@@ -447,5 +477,7 @@ void radio_menu(GtkWidget *parent) {
   sub_menu=dialog;
 
   gtk_widget_show_all(dialog);
+  // Only show this buttion if the C25 filter board is selected
+  if (filter_board != CHARLY25) gtk_widget_hide(use_tcp_b);
 
 }