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;
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");
socklen_t length;
unsigned char buffer[2048];
int bytes_read;
+ int ret,left;
int ep;
long sequence;
#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;
}
// 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;
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
}
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");
+ }
}
}
static GtkWidget *dialog=NULL;
+static GtkWidget *use_tcp_b=NULL;
+
static void cleanup() {
if(dialog!=NULL) {
gtk_widget_destroy(dialog);
}
}
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) {
}
}
+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;
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);
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);
}