From: c vw Date: Sun, 11 Nov 2018 12:55:11 +0000 (+0100) Subject: Add functionality to switch from UDP to TCP for CHARLY25 RedPitay-based X-Git-Url: https://git.rkrishnan.org/pf/content/en/seg/status?a=commitdiff_plain;h=1a65097c122e451f0f9d7cde0e49fd8241c7b730;p=pihpsdr.git Add functionality to switch from UDP to TCP for CHARLY25 RedPitay-based SDRs. Added support for this in the RADIO menu. --- diff --git a/old_protocol.c b/old_protocol.c index 99264c6..7803079 100644 --- a/old_protocol.c +++ b/old_protocol.c @@ -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 c9bb32c..471f189 100644 --- 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 943ed24..7f8d3aa 100644 --- 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; diff --git a/radio_menu.c b/radio_menu.c index fb915d6..7b1aef6 100644 --- a/radio_menu.c +++ b/radio_menu.c @@ -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); }