]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
Adding initial code for SoapySDR transmit
authorJohn Melton G0ORX <john.d.melton@googlemail.com>
Sat, 5 Oct 2019 14:18:33 +0000 (15:18 +0100)
committerJohn Melton G0ORX <john.d.melton@googlemail.com>
Sat, 5 Oct 2019 14:18:33 +0000 (15:18 +0100)
band.c
dac.h [new file with mode: 0644]
radio.c
radio.h
radio_menu.c

diff --git a/band.c b/band.c
index bc54e6821469555e4249d036661045284f3d6030..f47a96c590bd7930d7ddcd2b2dc94a3f64a65ddd 100644 (file)
--- a/band.c
+++ b/band.c
@@ -451,13 +451,22 @@ void bandSaveState() {
         setProperty(name,value);
 
         sprintf(value,"%lld",bands[b].frequencyLO);
-        sprintf(name,"band.%d.frequencyLO",b);
+        sprintf(name,"band.%d.txFrequencyLO",b);
         setProperty(name,value);
 
         sprintf(value,"%lld",bands[b].errorLO);
         sprintf(name,"band.%d.errorLO",b);
         setProperty(name,value);
 
+        sprintf(value,"%lld",bands[b].txFrequencyLO);
+        sprintf(name,"band.%d.txFrequencyLO",b);
+        setProperty(name,value);
+
+        sprintf(value,"%lld",bands[b].txErrorLO);
+        sprintf(name,"band.%d.txErrorLO",b);
+        setProperty(name,value);
+
+
         sprintf(value,"%d",bands[b].disablePA);
         sprintf(name,"band.%d.disablePA",b);
         setProperty(name,value);
@@ -575,6 +584,14 @@ void bandRestoreState() {
         value=getProperty(name);
         if(value) bands[b].errorLO=atoll(value);
 
+        sprintf(name,"band.%d.txFrequencyLO",b);
+        value=getProperty(name);
+        if(value) bands[b].txFrequencyLO=atoll(value);
+
+        sprintf(name,"band.%d.txErrorLO",b);
+        value=getProperty(name);
+        if(value) bands[b].txErrorLO=atoll(value);
+
         sprintf(name,"band.%d.disablePA",b);
         value=getProperty(name);
         if(value) bands[b].disablePA=atoi(value);
diff --git a/dac.h b/dac.h
new file mode 100644 (file)
index 0000000..5bd9ad4
--- /dev/null
+++ b/dac.h
@@ -0,0 +1,28 @@
+/* Copyright (C)
+* 2018 - John Melton, G0ORX/N6LYT
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+*
+*/
+
+#ifndef DAC_H
+#define DAC_H
+
+typedef struct _dac {
+  gint antenna;
+  gint *tx_gain;
+} DAC;
+
+#endif
diff --git a/radio.c b/radio.c
index 47c6fde67572bfa9f6d719c87db0fd8eea480497..8bfe0d3a7d037bc6d96f3f637b941b3d345f7cb0 100644 (file)
--- a/radio.c
+++ b/radio.c
@@ -30,6 +30,7 @@
 #include <wdsp.h>
 
 #include "adc.h"
+#include "dac.h"
 #include "audio.h"
 #include "discovered.h"
 //#include "discovery.h"
@@ -635,15 +636,11 @@ void start_radio() {
       adc[0].rx_gain[i]=0;
     }
     adc[0].agc=FALSE;
-/*
-    if(r->can_transmit) {
-      r->dac[0].antenna=1;
-      r->dac[0].tx_gain=malloc(r->discovered->info.soapy.tx_gains*sizeof(gint));
-      for (size_t i = 0; i < r->discovered->info.soapy.tx_gains; i++) {
-        r->dac[0].tx_gain[i]=0;
-      }
+    dac[0].antenna=1;
+    dac[0].tx_gain=malloc(radio->info.soapy.tx_gains*sizeof(gint));
+    for (size_t i = 0; i < radio->info.soapy.tx_gains; i++) {
+      dac[0].tx_gain[i]=0;
     }
-*/
   }
 #endif
 
@@ -664,14 +661,10 @@ void start_radio() {
     }
     adc[1].agc=FALSE;
 
-/*
-    if(r->can_transmit) {
-      r->dac[1].tx_gain=malloc(r->discovered->info.soapy.tx_gains*sizeof(gint));
-      for (size_t i = 0; i < r->discovered->info.soapy.tx_gains; i++) {
-        r->dac[1].tx_gain[i]=0;
-      }
+    dac[1].tx_gain=malloc(radio->info.soapy.tx_gains*sizeof(gint));
+    for (size_t i = 0; i < radio->info.soapy.tx_gains; i++) {
+      dac[1].tx_gain[i]=0;
     }
-*/
   }
 
   radio_sample_rate=radio->info.soapy.sample_rate;
@@ -904,14 +897,13 @@ void start_radio() {
       soapy_protocol_set_gain(rx,radio->info.soapy.rx_gain[i],adc[0].rx_gain[i]);
     }
     soapy_protocol_start_receiver(rx);
-/*
-    if(r->can_transmit) {
-      if(r->transmitter!=NULL && r->transmitter->rx==rx) {
-        soapy_protocol_set_tx_antenna(r->transmitter,1);
-        soapy_protocol_set_tx_frequency(r->transmitter);
+
+    if(transmitter!=NULL) {
+      soapy_protocol_set_tx_antenna(transmitter,dac[0].antenna);
+      for(int i=0;i<radio->info.soapy.tx_gains;i++) {
+        soapy_protocol_set_tx_gain(transmitter,radio->info.soapy.tx_gain[i],dac[0].tx_gain[i]);
       }
     }
-*/
   }
 #endif
 
@@ -1622,15 +1614,11 @@ fprintf(stderr,"sem_wait: returner\n");
     value=getProperty("radio.adc[0].antenna");
     if(value!=NULL) adc[0].antenna=atoi(value);
 
-/*
-    if(radio->can_transmit) {
-      for(int i=0;i<radio->info.soapy.tx_gains;i++) {
-        sprintf(name,"radio.dac[0].tx_gain.%s",radio->info.soapy.tx_gain[i]);
-        value=getProperty(name);
-        if(value!=NULL) dac[0].tx_gain[i]=atoi(value);
-      }
+    for(int i=0;i<radio->info.soapy.tx_gains;i++) {
+      sprintf(name,"radio.dac[0].tx_gain.%s",radio->info.soapy.tx_gain[i]);
+      value=getProperty(name);
+      if(value!=NULL) dac[0].tx_gain[i]=atoi(value);
     }
-*/
   }
 #endif
 
@@ -1827,15 +1815,12 @@ fprintf(stderr,"sem_wait: returned\n");
       sprintf(value,"%d", adc[0].antenna);
       setProperty(name,value);
 
-/*
-      if(radio->can_transmit) {
-        for(int i=0;i<radio->discovered->info.soapy.tx_gains;i++) {
-          sprintf(name,"radio.dac[0].tx_gain.%s",radio->discovered->info.soapy.tx_gain[i]);
-          sprintf(value,"%d", radio->dac[0].tx_gain[i]);
-          setProperty(name,value);
-        }
+      for(int i=0;i<radio->info.soapy.tx_gains;i++) {
+        sprintf(name,"radio.dac[0].tx_gain.%s",radio->info.soapy.tx_gain[i]);
+        sprintf(value,"%d", dac[0].tx_gain[i]);
+        setProperty(name,value);
       }
-*/
+
       for(int i=0;i<radio->info.soapy.rx_gains;i++) {
         sprintf(name,"radio.adc[1].rx_gain.%s",radio->info.soapy.rx_gain[i]);
         sprintf(value,"%d", adc[1].rx_gain[i]);
@@ -1848,15 +1833,11 @@ fprintf(stderr,"sem_wait: returned\n");
       sprintf(value,"%d", adc[1].antenna);
       setProperty(name,value);
 
-/*
-      if(radio->can_transmit) {
-        for(int i=0;i<radio->discovered->info.soapy.tx_gains;i++) {
-          sprintf(name,"radio.dac[1].tx_gain.%s",radio->discovered->info.soapy.tx_gain[i]);
-          sprintf(value,"%d", radio->dac[1].tx_gain[i]);
-          setProperty(name,value);
-        }
+      for(int i=0;i<radio->info.soapy.tx_gains;i++) {
+        sprintf(name,"radio.dac[1].tx_gain.%s",radio->info.soapy.tx_gain[i]);
+        sprintf(value,"%d", dac[1].tx_gain[i]);
+        setProperty(name,value);
       }
-*/
     }
 #endif
 
diff --git a/radio.h b/radio.h
index f10902e7033d0fca40c34db4628c071a01871954..85a7431320722b5f04da5be53636ec1980865a13 100644 (file)
--- a/radio.h
+++ b/radio.h
@@ -21,6 +21,7 @@
 #define _RADIO_H
 
 #include "adc.h"
+#include "dac.h"
 #include "discovered.h"
 #include "receiver.h"
 #include "transmitter.h"
@@ -151,6 +152,7 @@ extern int mic_ptt_tip_bias_ring;
 int receivers;
 
 ADC adc[2];
+DAC dac[2];
 int adc_attenuation[2];
 int rx_gain_slider[2];
 
index 4f6b87f5ab4ffe46ef0788a05346b5878c304ce4..95f5814e2b3f0f0fbe3620533ba5d4185e1accf8 100644 (file)
@@ -64,7 +64,7 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d
 }
 
 #ifdef SOAPYSDR
-static void gain_value_changed_cb(GtkWidget *widget, gpointer data) {
+static void rx_gain_value_changed_cb(GtkWidget *widget, gpointer data) {
   ADC *adc=(ADC *)data;
   int gain;
   if(radio->device==SOAPYSDR_USB_DEVICE) {
@@ -79,6 +79,22 @@ static void gain_value_changed_cb(GtkWidget *widget, gpointer data) {
   }
 }
 
+static void tx_gain_value_changed_cb(GtkWidget *widget, gpointer data) {
+  DAC *dac=(DAC *)data;
+  int gain;
+  if(radio->device==SOAPYSDR_USB_DEVICE) {
+    gain=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+    soapy_protocol_set_tx_gain(transmitter,(char *)gtk_widget_get_name(widget),gain);
+    for(int i=0;i<radio->info.soapy.tx_gains;i++) {
+      if(strcmp(radio->info.soapy.tx_gain[i],(char *)gtk_widget_get_name(widget))==0) {
+        dac[0].tx_gain[i]=gain;
+        break;
+      }
+    }
+  }
+}
+
+
 static void agc_changed_cb(GtkWidget *widget, gpointer data) {
   ADC *adc=(ADC *)data;
   gboolean agc=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
@@ -251,81 +267,105 @@ void radio_menu(GtkWidget *parent) {
   GtkWidget *grid=gtk_grid_new();
   gtk_grid_set_column_spacing (GTK_GRID(grid),10);
 
+  int col=0;
+  int row=0;
+  int temp_row=0;
+
   GtkWidget *close_b=gtk_button_new_with_label("Close");
   g_signal_connect (close_b, "button_press_event", G_CALLBACK(close_cb), NULL);
-  gtk_grid_attach(GTK_GRID(grid),close_b,0,0,1,1);
+  gtk_grid_attach(GTK_GRID(grid),close_b,col,row,1,1);
+
+  col++;
 
   GtkWidget *region_label=gtk_label_new("Region: ");
-  gtk_grid_attach(GTK_GRID(grid),region_label,1,0,1,1);
+  gtk_grid_attach(GTK_GRID(grid),region_label,col,row,1,1);
   
+  col++;
+
   GtkWidget *region_combo=gtk_combo_box_text_new();
   gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(region_combo),NULL,"Other");
   gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(region_combo),NULL,"UK");
   gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(region_combo),NULL,"WRC15");
   gtk_combo_box_set_active(GTK_COMBO_BOX(region_combo),region);
-  gtk_grid_attach(GTK_GRID(grid),region_combo,2,0,1,1);
+  gtk_grid_attach(GTK_GRID(grid),region_combo,col,row,1,1);
   g_signal_connect(region_combo,"changed",G_CALLBACK(region_cb),NULL);
 
-  int x=0;
+
+  row++;
+  col=0;
 
   GtkWidget *receivers_label=gtk_label_new("Receivers: ");
-  gtk_grid_attach(GTK_GRID(grid),receivers_label,x,1,1,1);
+  gtk_grid_attach(GTK_GRID(grid),receivers_label,col,row,1,1);
+
+  row++;
   
   GtkWidget *receivers_1=gtk_radio_button_new_with_label(NULL,"1");
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (receivers_1), receivers==1);
-  gtk_grid_attach(GTK_GRID(grid),receivers_1,x,2,1,1);
+  gtk_grid_attach(GTK_GRID(grid),receivers_1,col,row,1,1);
   g_signal_connect(receivers_1,"pressed",G_CALLBACK(receivers_cb),(gpointer *)1);
 
+  row++;
+
   GtkWidget *receivers_2=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(receivers_1),"2");
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (receivers_2), receivers==2);
-  gtk_grid_attach(GTK_GRID(grid),receivers_2,x,3,1,1);
+  gtk_grid_attach(GTK_GRID(grid),receivers_2,col,row,1,1);
   g_signal_connect(receivers_2,"pressed",G_CALLBACK(receivers_cb),(gpointer *)2);
+  row++;
+  col++;
+  if(row>temp_row) temp_row=row;
 
-  x++;
+  row=1;
 
   switch(protocol) {
     case ORIGINAL_PROTOCOL:
       {
       GtkWidget *sample_rate_label=gtk_label_new("Sample Rate:");
-      gtk_grid_attach(GTK_GRID(grid),sample_rate_label,x,1,1,1);
+      gtk_grid_attach(GTK_GRID(grid),sample_rate_label,col,row,1,1);
+      row++;
 
       GtkWidget *sample_rate_48=gtk_radio_button_new_with_label(NULL,"48000");
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_48), active_receiver->sample_rate==48000);
-      gtk_grid_attach(GTK_GRID(grid),sample_rate_48,x,2,1,1);
+      gtk_grid_attach(GTK_GRID(grid),sample_rate_48,col,row,1,1);
       g_signal_connect(sample_rate_48,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)48000);
+      row++;
 
       GtkWidget *sample_rate_96=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_48),"96000");
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_96), active_receiver->sample_rate==96000);
-      gtk_grid_attach(GTK_GRID(grid),sample_rate_96,x,3,1,1);
+      gtk_grid_attach(GTK_GRID(grid),sample_rate_96,col,row,1,1);
       g_signal_connect(sample_rate_96,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)96000);
+      row++;
 
       GtkWidget *sample_rate_192=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_96),"192000");
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_192), active_receiver->sample_rate==192000);
-      gtk_grid_attach(GTK_GRID(grid),sample_rate_192,x,4,1,1);
+      gtk_grid_attach(GTK_GRID(grid),sample_rate_192,col,row,1,1);
       g_signal_connect(sample_rate_192,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)192000);
+      row++;
 
       GtkWidget *sample_rate_384=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_192),"384000");
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_384), active_receiver->sample_rate==384000);
-      gtk_grid_attach(GTK_GRID(grid),sample_rate_384,x,5,1,1);
+      gtk_grid_attach(GTK_GRID(grid),sample_rate_384,col,row,1,1);
       g_signal_connect(sample_rate_384,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)384000);
+      row++;
 
       if(protocol==NEW_PROTOCOL) {
         GtkWidget *sample_rate_768=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_384),"768000");
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_768), active_receiver->sample_rate==768000);
-        gtk_grid_attach(GTK_GRID(grid),sample_rate_768,x,6,1,1);
+        gtk_grid_attach(GTK_GRID(grid),sample_rate_768,col,row,1,1);
         g_signal_connect(sample_rate_768,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)768000);
+        row++;
   
         GtkWidget *sample_rate_1536=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_768),"1536000");
         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_1536), active_receiver->sample_rate==1536000);
-          gtk_grid_attach(GTK_GRID(grid),sample_rate_1536,x,7,1,1);
+        gtk_grid_attach(GTK_GRID(grid),sample_rate_1536,col,row,1,1);
         g_signal_connect(sample_rate_1536,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)1536000);
+        row++;
   
 #ifdef GPIO
         gtk_widget_set_sensitive(sample_rate_768,FALSE);
         gtk_widget_set_sensitive(sample_rate_1536,FALSE);
 #endif
       }
-      x++;
+      col++;
       }
       break;
   
@@ -333,23 +373,28 @@ void radio_menu(GtkWidget *parent) {
     case SOAPYSDR_PROTOCOL:
       {
       GtkWidget *sample_rate_label=gtk_label_new("Sample Rate:");
-      gtk_grid_attach(GTK_GRID(grid),sample_rate_label,x,1,1,1);
+      gtk_grid_attach(GTK_GRID(grid),sample_rate_label,col,row,1,1);
+      row++;
 
       char rate[16];
       sprintf(rate,"%d",radio->info.soapy.sample_rate);
 
       GtkWidget *sample_rate=gtk_radio_button_new_with_label(NULL,rate);
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate), radio->info.soapy.sample_rate);
-      gtk_grid_attach(GTK_GRID(grid),sample_rate,x,2,1,1);
+      gtk_grid_attach(GTK_GRID(grid),sample_rate,col,row,1,1);
       g_signal_connect(sample_rate,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)radio->info.soapy.sample_rate);
 
-      x++;
+      col++;
       }
       break;
 #endif
 
   }
+  row++;
+  if(row>temp_row) temp_row=row;
+
 
+  row=1;
 
   if(protocol==ORIGINAL_PROTOCOL || protocol==NEW_PROTOCOL) {
 
@@ -360,88 +405,114 @@ void radio_menu(GtkWidget *parent) {
 
       GtkWidget *ptt_ring_b=gtk_radio_button_new_with_label(NULL,"PTT On Ring, Mic and Bias on Tip");
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_ring_b), mic_ptt_tip_bias_ring==0);
-      gtk_grid_attach(GTK_GRID(grid),ptt_ring_b,x,1,1,1);
+      gtk_grid_attach(GTK_GRID(grid),ptt_ring_b,col,row,1,1);
       g_signal_connect(ptt_ring_b,"toggled",G_CALLBACK(ptt_ring_cb),NULL);
+      row++;
 
       GtkWidget *ptt_tip_b=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ptt_ring_b),"PTT On Tip, Mic and Bias on Ring");
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_tip_b), mic_ptt_tip_bias_ring==1);
-      gtk_grid_attach(GTK_GRID(grid),ptt_tip_b,x,2,1,1);
+      gtk_grid_attach(GTK_GRID(grid),ptt_tip_b,col,row,1,1);
       g_signal_connect(ptt_tip_b,"toggled",G_CALLBACK(ptt_tip_cb),NULL);
+      row++;
 
       GtkWidget *ptt_b=gtk_check_button_new_with_label("PTT Enabled");
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_b), mic_ptt_enabled);
-      gtk_grid_attach(GTK_GRID(grid),ptt_b,x,3,1,1);
+      gtk_grid_attach(GTK_GRID(grid),ptt_b,col,row,1,1);
       g_signal_connect(ptt_b,"toggled",G_CALLBACK(ptt_cb),NULL);
+      row++;
 
       GtkWidget *bias_b=gtk_check_button_new_with_label("BIAS Enabled");
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bias_b), mic_bias_enabled);
-      gtk_grid_attach(GTK_GRID(grid),bias_b,x,4,1,1);
+      gtk_grid_attach(GTK_GRID(grid),bias_b,col,row,1,1);
       g_signal_connect(bias_b,"toggled",G_CALLBACK(bias_cb),NULL);
+      row++;
 
-      x++;
+      if(row>temp_row) temp_row=row;
+      col++;
     }
 
+    row=1;
+
     GtkWidget *sample_rate_label=gtk_label_new("Filter Board:");
-    gtk_grid_attach(GTK_GRID(grid),sample_rate_label,x,1,1,1);
+    gtk_grid_attach(GTK_GRID(grid),sample_rate_label,col,row,1,1);
+    row++;
 
     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, 2, 1, 1);
+    gtk_grid_attach(GTK_GRID(grid), none_b, col, row, 1, 1);
+    row++;
 
     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, 3, 1, 1);
+    gtk_grid_attach(GTK_GRID(grid), alex_b, col, row, 1, 1);
+    row++;
 
     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, 4, 1, 1);
+    gtk_grid_attach(GTK_GRID(grid), apollo_b, col, row, 1, 1);
+    row++;
 
     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, 5, 1, 1);
+    gtk_grid_attach(GTK_GRID(grid), charly25_b, col, row, 1, 1);
+    row++;
 
     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);
 
-    x++;
+    if(row>temp_row) temp_row=row;
+    col++;
   }
 
+  row=1;
+
   GtkWidget *rit_label=gtk_label_new("RIT step (Hz): ");
-  gtk_grid_attach(GTK_GRID(grid),rit_label,x,1,1,1);
+  gtk_grid_attach(GTK_GRID(grid),rit_label,col,row,1,1);
+  row++;
 
   GtkWidget *rit_1=gtk_radio_button_new_with_label(NULL,"1");
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rit_1), rit_increment==1);
-  gtk_grid_attach(GTK_GRID(grid),rit_1,x,2,1,1);
+  gtk_grid_attach(GTK_GRID(grid),rit_1,col,row,1,1);
   g_signal_connect(rit_1,"pressed",G_CALLBACK(rit_cb),(gpointer *)1);
+  row++;
 
   GtkWidget *rit_10=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(rit_1),"10");
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rit_10), rit_increment==10);
-  gtk_grid_attach(GTK_GRID(grid),rit_10,x,3,1,1);
+  gtk_grid_attach(GTK_GRID(grid),rit_10,col,row,1,1);
   g_signal_connect(rit_10,"pressed",G_CALLBACK(rit_cb),(gpointer *)10);
+  row++;
 
   GtkWidget *rit_100=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(rit_10),"100");
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rit_100), rit_increment==100);
-  gtk_grid_attach(GTK_GRID(grid),rit_100,x,4,1,1);
+  gtk_grid_attach(GTK_GRID(grid),rit_100,col,row,1,1);
   g_signal_connect(rit_100,"pressed",G_CALLBACK(rit_cb),(gpointer *)100);
+  row++;
 
-  x++;
+  if(row>temp_row) temp_row=row;
+  col++;
+  row=1;
 
   GtkWidget *vfo_divisor_label=gtk_label_new("VFO Encoder Divisor: ");
-  gtk_grid_attach(GTK_GRID(grid),vfo_divisor_label,x,1,1,1);
+  gtk_grid_attach(GTK_GRID(grid),vfo_divisor_label,col,row,1,1);
+  row++;
 
   GtkWidget *vfo_divisor=gtk_spin_button_new_with_range(1.0,60.0,1.0);
   gtk_spin_button_set_value(GTK_SPIN_BUTTON(vfo_divisor),(double)vfo_encoder_divisor);
-  gtk_grid_attach(GTK_GRID(grid),vfo_divisor,x,2,1,1);
+  gtk_grid_attach(GTK_GRID(grid),vfo_divisor,col,row,1,1);
   g_signal_connect(vfo_divisor,"value_changed",G_CALLBACK(vfo_divisor_value_changed_cb),NULL);
-
+  row++;
+   
   GtkWidget *iqswap_b=gtk_check_button_new_with_label("Swap IQ");
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (iqswap_b), iqswap);
-  gtk_grid_attach(GTK_GRID(grid),iqswap_b,x,4,1,1);
+  gtk_grid_attach(GTK_GRID(grid),iqswap_b,col,row,1,1);
   g_signal_connect(iqswap_b,"toggled",G_CALLBACK(iqswap_cb),NULL);
+  row++;
 
-  x++;
+  if(row>temp_row) temp_row=row;
+
+  col++;
 
 #ifdef USBOZY
   if (protocol==ORIGINAL_PROTOCOL && (device == DEVICE_OZY) || (device == DEVICE_METIS))
@@ -449,72 +520,117 @@ void radio_menu(GtkWidget *parent) {
   if (protocol==ORIGINAL_PROTOCOL && radio->device == DEVICE_METIS)
 #endif
   {
+    row=1;
     GtkWidget *atlas_label=gtk_label_new("Atlas bus: ");
-    gtk_grid_attach(GTK_GRID(grid),atlas_label,x,1,1,1);
+    gtk_grid_attach(GTK_GRID(grid),atlas_label,col,row,1,1);
+    row++;
 
     GtkWidget *ck10mhz_1=gtk_radio_button_new_with_label(NULL,"10MHz clock=Atlas");
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ck10mhz_1), atlas_clock_source_10mhz==0);
-    gtk_grid_attach(GTK_GRID(grid),ck10mhz_1,x,2,1,1);
+    gtk_grid_attach(GTK_GRID(grid),ck10mhz_1,col,row,1,1);
     g_signal_connect(ck10mhz_1,"toggled",G_CALLBACK(ck10mhz_cb),(gpointer *)0);
+    row++;
 
     GtkWidget *ck10mhz_2=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ck10mhz_1),"10MHz clock=Penelope");
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ck10mhz_2), atlas_clock_source_10mhz==1);
-    gtk_grid_attach(GTK_GRID(grid),ck10mhz_2,x,3,1,1);
+    gtk_grid_attach(GTK_GRID(grid),ck10mhz_2,col,row,1,1);
     g_signal_connect(ck10mhz_2,"toggled",G_CALLBACK(ck10mhz_cb),(gpointer *)1);
+    row++;
 
     GtkWidget *ck10mhz_3=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ck10mhz_2),"10MHz clock=Mercury");
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ck10mhz_3), atlas_clock_source_10mhz==2);
-    gtk_grid_attach(GTK_GRID(grid),ck10mhz_3,x,4,1,1);
+    gtk_grid_attach(GTK_GRID(grid),ck10mhz_3,col,row,1,1);
     g_signal_connect(ck10mhz_3,"toggled",G_CALLBACK(ck10mhz_cb),(gpointer *)2);
+    row++;
 
     GtkWidget *ck128_b=gtk_check_button_new_with_label("122.88MHz ck=Mercury");
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ck128_b), atlas_clock_source_128mhz);
-    gtk_grid_attach(GTK_GRID(grid),ck128_b,x,5,1,1);
+    gtk_grid_attach(GTK_GRID(grid),ck128_b,col,row,1,1);
     g_signal_connect(ck128_b,"toggled",G_CALLBACK(ck128mhz_cb),NULL);
+    row++;
 
     GtkWidget *mic_src_b=gtk_check_button_new_with_label("Mic src=Penelope");
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (mic_src_b), atlas_mic_source);
-    gtk_grid_attach(GTK_GRID(grid),mic_src_b,x,6,1,1);
+    gtk_grid_attach(GTK_GRID(grid),mic_src_b,col,row,1,1);
     g_signal_connect(mic_src_b,"toggled",G_CALLBACK(micsource_cb),NULL);
+    row++;
 
     GtkWidget *pene_tx_b=gtk_check_button_new_with_label("Penelope TX");
     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pene_tx_b), atlas_penelope);
-    gtk_grid_attach(GTK_GRID(grid),pene_tx_b,x,7,1,1);
+    gtk_grid_attach(GTK_GRID(grid),pene_tx_b,col,row,1,1);
     g_signal_connect(pene_tx_b,"toggled",G_CALLBACK(penelopetx_cb),NULL);
+    row++;
+
+    if(row>temp_row) temp_row=row;
   }
 
 #ifdef SOAPYSDR
+  row=temp_row;
+  col=0;
   if(radio->device==SOAPYSDR_USB_DEVICE) {
     int i;
     if(radio->info.soapy.rx_gains>0) {
-        x++;
-        GtkWidget *gain=gtk_label_new("Gains:");
-        gtk_grid_attach(GTK_GRID(grid),gain,0,x,1,1);
-        x++;
-      }
+      GtkWidget *rx_gain=gtk_label_new("Rx Gains:");
+      gtk_grid_attach(GTK_GRID(grid),rx_gain,col,row,1,1);
+      row++;
+    }
 
-      if(radio->info.soapy.rx_has_automatic_gain) {
-        GtkWidget *agc=gtk_check_button_new_with_label("Hardware AGC: ");
-        gtk_grid_attach(GTK_GRID(grid),agc,1,x,1,1);
-        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(agc),adc[0].agc);
-        g_signal_connect(agc,"toggled",G_CALLBACK(agc_changed_cb),&adc[0]);
-        x++;
+
+    if(radio->info.soapy.rx_has_automatic_gain) {
+      GtkWidget *agc=gtk_check_button_new_with_label("Hardware AGC: ");
+      gtk_grid_attach(GTK_GRID(grid),agc,col,row,1,1);
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(agc),adc[0].agc);
+      g_signal_connect(agc,"toggled",G_CALLBACK(agc_changed_cb),&adc[0]);
+      col++;
+    }
+
+    row=temp_row;
+
+    if(radio->info.soapy.tx_gains>0) {
+      col=2;
+      GtkWidget *tx_gain=gtk_label_new("Tx Gains:");
+      gtk_grid_attach(GTK_GRID(grid),tx_gain,col,row,1,1);
+      row++;
+    }
+
+    temp_row=row;
+
+    for(i=0;i<radio->info.soapy.rx_gains;i++) {
+      col=0;
+      GtkWidget *rx_gain_label=gtk_label_new(radio->info.soapy.rx_gain[i]);
+      gtk_grid_attach(GTK_GRID(grid),rx_gain_label,col,row,1,1);
+      col++;
+      SoapySDRRange range=radio->info.soapy.rx_range[i];
+      if(range.step==0.0) {
+        range.step=1.0;
       }
+      GtkWidget *rx_gain_b=gtk_spin_button_new_with_range(range.minimum,range.maximum,range.step);
+      gtk_widget_set_name (rx_gain_b, radio->info.soapy.rx_gain[i]);
+      gtk_spin_button_set_value(GTK_SPIN_BUTTON(rx_gain_b),(double)adc[0].rx_gain[i]);
+      gtk_grid_attach(GTK_GRID(grid),rx_gain_b,col,row,1,1);
+      g_signal_connect(rx_gain_b,"value_changed",G_CALLBACK(rx_gain_value_changed_cb),&adc[0]);
+      col++;
+      row++;
+    }
 
-      for(i=0;i<radio->info.soapy.rx_gains;i++) {
-        GtkWidget *gain_label=gtk_label_new(radio->info.soapy.rx_gain[i]);
-        gtk_grid_attach(GTK_GRID(grid),gain_label,0,x,1,1);
-        SoapySDRRange range=radio->info.soapy.rx_range[i];
-        if(range.step==0.0) {
-          range.step=1.0;
-        }
-        GtkWidget *gain_b=gtk_spin_button_new_with_range(range.minimum,range.maximum,range.step);
-        gtk_widget_set_name (gain_b, radio->info.soapy.rx_gain[i]);
-        gtk_spin_button_set_value(GTK_SPIN_BUTTON(gain_b),(double)adc[0].rx_gain[i]);
-        gtk_grid_attach(GTK_GRID(grid),gain_b,1,x,1,1);
-        g_signal_connect(gain_b,"value_changed",G_CALLBACK(gain_value_changed_cb),&adc[0]);
-        x++;
+    row=temp_row;
+
+    for(i=0;i<radio->info.soapy.tx_gains;i++) {
+      col=2;
+      GtkWidget *tx_gain_label=gtk_label_new(radio->info.soapy.tx_gain[i]);
+      gtk_grid_attach(GTK_GRID(grid),tx_gain_label,col,row,1,1);
+      col++;
+      SoapySDRRange range=radio->info.soapy.tx_range[i];
+      if(range.step==0.0) {
+        range.step=1.0;
       }
+      GtkWidget *tx_gain_b=gtk_spin_button_new_with_range(range.minimum,range.maximum,range.step);
+      gtk_widget_set_name (tx_gain_b, radio->info.soapy.tx_gain[i]);
+      gtk_spin_button_set_value(GTK_SPIN_BUTTON(tx_gain_b),(double)dac[0].tx_gain[i]);
+      gtk_grid_attach(GTK_GRID(grid),tx_gain_b,col,row,1,1);
+      g_signal_connect(tx_gain_b,"value_changed",G_CALLBACK(tx_gain_value_changed_cb),&adc[0]);
+      row++;
+    }
 
   }
 #endif