]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
Added ZOOM and PAN capability. Added external PTT GPIO input.
authorJohn Melton G0ORX <john.d.melton@googlemail.com>
Sat, 14 Mar 2020 13:43:11 +0000 (13:43 +0000)
committerJohn Melton G0ORX <john.d.melton@googlemail.com>
Sat, 14 Mar 2020 13:43:11 +0000 (13:43 +0000)
28 files changed:
configure.c
discovery.c
display_menu.c
encoder_menu.c
ext.c
ext.h
gpio.c
gpio.h
i2c.c
midi.h
midi2.c
midi3.c
new_protocol.c
old_protocol.c
radio.c
radio.h
receiver.c
receiver.h
rx_panadapter.c
sliders.c
sliders.h
soapy_discovery.c
soapy_protocol.c
soapy_protocol.h
vfo.c
waterfall.c
zoompan.c [new file with mode: 0644]
zoompan.h [new file with mode: 0644]

index c9c496d1be7e58e3b33c55fb1685f43acc57352b..b3deb42bf8c75ddfac22e7f03f204f0ff2b69e6c 100644 (file)
@@ -139,6 +139,13 @@ static GtkWidget *b_enable_cwlr;
 static GtkWidget *b_cw_active_low;
 #endif
 
+#ifdef PTT
+static GtkWidget *ptt_label;
+static GtkWidget *ptt;
+static GtkWidget *b_enable_ptt;
+static GtkWidget *b_ptt_active_low;
+#endif
+
 static gboolean save_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
   if(dialog!=NULL) {
     ENABLE_VFO_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_vfo_encoder))?1:0;
@@ -164,7 +171,7 @@ static gboolean save_cb (GtkWidget *widget, GdkEventButton *event, gpointer data
     ENABLE_E4_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E4_pullup))?1:0;
     E4_FUNCTION=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E4_sw));
 
-    if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2 || controller==CUSTOM_CONTROLLER) {
+    if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
       ENABLE_E5_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E5_encoder))?1:0;
       E5_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E5_a));
       E5_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E5_b));
@@ -215,6 +222,12 @@ static gboolean save_cb (GtkWidget *widget, GdkEventButton *event, gpointer data
     SIDETONE_GPIO=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cws));
 #endif
 
+#ifdef PTT
+    ENABLE_PTT_GPIO=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_ptt))?1:0;
+    PTT_ACTIVE_LOW=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_ptt_active_low))?1:0;
+    PTT_GPIO=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ptt));
+#endif
+
     gpio_save_state();
     gtk_widget_destroy(dialog);
   }
@@ -258,7 +271,9 @@ void configure_gpio(GtkWidget *parent) {
     // Encoders
   
     GtkWidget *grid1=gtk_grid_new();
-    gtk_grid_set_column_spacing (GTK_GRID(grid1),10);
+    gtk_grid_set_column_homogeneous(GTK_GRID(grid1),FALSE);
+    gtk_grid_set_row_homogeneous(GTK_GRID(grid1),TRUE);
+    gtk_grid_set_column_spacing (GTK_GRID(grid1),5);
     row=0;
   
     b_enable_vfo_encoder=gtk_check_button_new_with_label("Enable VFO");
@@ -410,7 +425,7 @@ void configure_gpio(GtkWidget *parent) {
   
     row++;
   
-    if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2 || controller==CUSTOM_CONTROLLER) {
+    if(controller==CONTROLLER2_V1 || controller==CONTROLLER2_V2) {
       b_enable_E5_encoder=gtk_check_button_new_with_label("Enable E5");
       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_E5_encoder), ENABLE_E5_ENCODER);
       gtk_widget_show(b_enable_E5_encoder);
@@ -701,6 +716,37 @@ void configure_gpio(GtkWidget *parent) {
   gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid3,gtk_label_new("CW"));
 #endif
 
+#ifdef PTT
+  GtkWidget *grid4=gtk_grid_new();
+  gtk_grid_set_column_spacing (GTK_GRID(grid4),10);
+  row=0;
+
+  b_enable_ptt=gtk_check_button_new_with_label("PTT Enable");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_ptt), ENABLE_PTT_GPIO);
+  gtk_widget_show(b_enable_ptt);
+  gtk_grid_attach(GTK_GRID(grid4),b_enable_ptt,0,row,1,1);
+
+  row++;
+
+  ptt_label=gtk_label_new("PTT GPIO:");
+  gtk_widget_show(ptt_label);
+  gtk_grid_attach(GTK_GRID(grid4),ptt_label,0,row,1,1);
+
+  ptt=gtk_spin_button_new_with_range (0.0,100.0,1.0);
+  gtk_spin_button_set_value (GTK_SPIN_BUTTON(ptt),CWR_BUTTON);
+  gtk_widget_show(ptt);
+  gtk_grid_attach(GTK_GRID(grid4),ptt,1,row,1,1);
+
+  row++;
+
+  b_ptt_active_low=gtk_check_button_new_with_label("PTT active-low");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_ptt_active_low), PTT_ACTIVE_LOW);
+  gtk_widget_show(b_ptt_active_low);
+  gtk_grid_attach(GTK_GRID(grid4),b_ptt_active_low,0,row,1,1);
+
+  gtk_notebook_append_page(GTK_NOTEBOOK(notebook),grid4,gtk_label_new("PTT"));
+#endif
+
   gtk_grid_attach(GTK_GRID(grid0),notebook,0,1,6,1);
   gtk_container_add(GTK_CONTAINER(content),grid0);
 
index 5252ff0f4ed9b5bf90c6563c3b506a3de89cef16..8b3a870fbb5505294463a175dcae0c26a8f46d53 100644 (file)
@@ -67,6 +67,12 @@ GtkWidget *tcpaddr;
 static char ipaddr_tcp_buf[IPADDR_LEN] = "10.10.10.10";
 char *ipaddr_tcp = &ipaddr_tcp_buf[0];
 
+#ifdef SERVER
+GtkWidget *connect_addr_entry;
+static char connect_addr_buffer[30]="0.0.0.0:50000";
+char *connect_addr = &connect_addr_buffer[0];
+#endif
+
 static gboolean delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data) {
   _exit(0);
 }
@@ -256,6 +262,15 @@ static gboolean tcp_cb (GtkWidget *widget, GdkEventButton *event, gpointer data)
            return TRUE;
        }
 
+#ifdef SERVER
+static gboolean connect_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
+  // connect to remote host running piHPSDR
+  strncpy(connect_addr, gtk_entry_get_text(GTK_ENTRY(connect_addr_entry)), 30);
+g_print("connect_cb: %s\n",connect_addr);
+  return TRUE;
+}
+#endif
+
 void discovery() {
 //fprintf(stderr,"discovery\n");
 
@@ -401,11 +416,11 @@ void discovery() {
     //gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
     gtk_grid_set_row_spacing (GTK_GRID(grid),10);
 
-    int i;
+    int row;
     char version[16];
     char text[256];
-    for(i=0;i<devices;i++) {
-      d=&discovered[i];
+    for(row=0;row<devices;row++) {
+      d=&discovered[row];
 fprintf(stderr,"%p Protocol=%d name=%s\n",d,d->protocol,d->name);
       sprintf(version,"v%d.%d",
                         d->software_version/10,
@@ -460,12 +475,12 @@ fprintf(stderr,"%p Protocol=%d name=%s\n",d,d->protocol,d->name);
       gtk_widget_override_font(label, pango_font_description_from_string("Sans 11"));
       gtk_widget_set_halign (label, GTK_ALIGN_START);
       gtk_widget_show(label);
-      gtk_grid_attach(GTK_GRID(grid),label,0,i,3,1);
+      gtk_grid_attach(GTK_GRID(grid),label,0,row,3,1);
 
       GtkWidget *start_button=gtk_button_new_with_label("Start");
       gtk_widget_override_font(start_button, pango_font_description_from_string("Sans 16"));
       gtk_widget_show(start_button);
-      gtk_grid_attach(GTK_GRID(grid),start_button,3,i,1,1);
+      gtk_grid_attach(GTK_GRID(grid),start_button,3,row,1,1);
       g_signal_connect(start_button,"button_press_event",G_CALLBACK(start_cb),(gpointer)d);
 
       // if not available then cannot start it
@@ -492,37 +507,37 @@ fprintf(stderr,"%p Protocol=%d name=%s\n",d,d->protocol,d->name);
           gtk_button_set_label(GTK_BUTTON(start_button), "Not installed");
           gtk_widget_set_sensitive(start_button, FALSE);
         } else {
-          apps_combobox[i] = gtk_combo_box_text_new();
-          gtk_widget_override_font(apps_combobox[i], pango_font_description_from_string("Sans 11"));
+          apps_combobox[row] = gtk_combo_box_text_new();
+          gtk_widget_override_font(apps_combobox[row], pango_font_description_from_string("Sans 11"));
           // We want the default selection priority for the STEMlab app to be
           // RP-Trx > HAMlab-Trx > Pavel-Trx > Pavel-Rx, so we add in decreasing order and
           // always set the newly added entry to be active.
           if ((d->software_version & STEMLAB_PAVEL_RX) != 0) {
-            gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(apps_combobox[i]),
+            gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(apps_combobox[row]),
                 "sdr_receiver_hpsdr", "Pavel-Rx");
-            gtk_combo_box_set_active_id(GTK_COMBO_BOX(apps_combobox[i]),
+            gtk_combo_box_set_active_id(GTK_COMBO_BOX(apps_combobox[row]),
                 "sdr_receiver_hpsdr");
           }
           if ((d->software_version & STEMLAB_PAVEL_TRX) != 0) {
-            gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(apps_combobox[i]),
+            gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(apps_combobox[row]),
                 "sdr_transceiver_hpsdr", "Pavel-Trx");
-            gtk_combo_box_set_active_id(GTK_COMBO_BOX(apps_combobox[i]),
+            gtk_combo_box_set_active_id(GTK_COMBO_BOX(apps_combobox[row]),
                 "sdr_transceiver_hpsdr");
           }
           if ((d->software_version & HAMLAB_RP_TRX) != 0) {
-            gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(apps_combobox[i]),
+            gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(apps_combobox[row]),
                 "hamlab_sdr_transceiver_hpsdr", "HAMlab-Trx");
-            gtk_combo_box_set_active_id(GTK_COMBO_BOX(apps_combobox[i]),
+            gtk_combo_box_set_active_id(GTK_COMBO_BOX(apps_combobox[row]),
                 "hamlab_sdr_transceiver_hpsdr");
           }
           if ((d->software_version & STEMLAB_RP_TRX) != 0) {
-            gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(apps_combobox[i]),
+            gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(apps_combobox[row]),
                 "stemlab_sdr_transceiver_hpsdr", "STEMlab-Trx");
-            gtk_combo_box_set_active_id(GTK_COMBO_BOX(apps_combobox[i]),
+            gtk_combo_box_set_active_id(GTK_COMBO_BOX(apps_combobox[row]),
                 "stemlab_sdr_transceiver_hpsdr");
           }
-          gtk_widget_show(apps_combobox[i]);
-          gtk_grid_attach(GTK_GRID(grid), apps_combobox[i], 4, i, 1, 1);
+          gtk_widget_show(apps_combobox[row]);
+          gtk_grid_attach(GTK_GRID(grid), apps_combobox[row], 4, i, 1, 1);
         }
       }
 #endif
@@ -538,8 +553,7 @@ fprintf(stderr,"%p Protocol=%d name=%s\n",d,d->protocol,d->name);
     gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(gpio),NULL,"Controller1");
     gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(gpio),NULL,"Controller2 V1");
     gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(gpio),NULL,"Controller2 V2");
-    gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(gpio),NULL,"Custom");
-    gtk_grid_attach(GTK_GRID(grid),gpio,0,i,1,1);
+    gtk_grid_attach(GTK_GRID(grid),gpio,0,row,1,1);
 
     gtk_combo_box_set_active(GTK_COMBO_BOX(gpio),controller);
     g_signal_connect(gpio,"changed",G_CALLBACK(gpio_changed_cb),NULL);
@@ -547,38 +561,53 @@ fprintf(stderr,"%p Protocol=%d name=%s\n",d,d->protocol,d->name);
 
     GtkWidget *discover_b=gtk_button_new_with_label("Discover");
     g_signal_connect (discover_b, "button-press-event", G_CALLBACK(discover_cb), NULL);
-    gtk_grid_attach(GTK_GRID(grid),discover_b,1,i,1,1);
+    gtk_grid_attach(GTK_GRID(grid),discover_b,1,row,1,1);
 
     GtkWidget *protocols_b=gtk_button_new_with_label("Protocols");
     g_signal_connect (protocols_b, "button-press-event", G_CALLBACK(protocols_cb), NULL);
-    gtk_grid_attach(GTK_GRID(grid),protocols_b,2,i,1,1);
+    gtk_grid_attach(GTK_GRID(grid),protocols_b,2,row,1,1);
 
 #ifdef MIDI
     GtkWidget *midi_b=gtk_button_new_with_label("ImportMIDI");
     g_signal_connect (midi_b, "button-press-event", G_CALLBACK(midi_cb), NULL);
-    gtk_grid_attach(GTK_GRID(grid),midi_b,3,i,1,1);
+    gtk_grid_attach(GTK_GRID(grid),midi_b,3,row,1,1);
 #endif
 
-    i++;
+    row++;
 
 #ifdef GPIO
     GtkWidget *gpio_b=gtk_button_new_with_label("Configure GPIO");
     g_signal_connect (gpio_b, "button-press-event", G_CALLBACK(gpio_cb), NULL);
-    gtk_grid_attach(GTK_GRID(grid),gpio_b,0,i,1,1);
+    gtk_grid_attach(GTK_GRID(grid),gpio_b,0,row,1,1);
 #endif
 
     GtkWidget *tcp_b=gtk_button_new_with_label("Use new TCP Addr:");
     g_signal_connect (tcp_b, "button-press-event", G_CALLBACK(tcp_cb), NULL);
-    gtk_grid_attach(GTK_GRID(grid),tcp_b,1,i,1,1);
+    gtk_grid_attach(GTK_GRID(grid),tcp_b,1,row,1,1);
 
     tcpaddr=gtk_entry_new();
     gtk_entry_set_max_length(GTK_ENTRY(tcpaddr), 20);
-    gtk_grid_attach(GTK_GRID(grid),tcpaddr,2,i,1,1);
+    gtk_grid_attach(GTK_GRID(grid),tcpaddr,2,row,1,1);
     gtk_entry_set_text(GTK_ENTRY(tcpaddr), ipaddr_tcp);
 
     GtkWidget *exit_b=gtk_button_new_with_label("Exit");
     g_signal_connect (exit_b, "button-press-event", G_CALLBACK(exit_cb), NULL);
-    gtk_grid_attach(GTK_GRID(grid),exit_b,3,i,1,1);
+    gtk_grid_attach(GTK_GRID(grid),exit_b,3,row,1,1);
+
+#ifdef SERVER
+    row++;
+
+    GtkWidget *connect_b=gtk_button_new_with_label("Connect (Addr:Port)");
+    g_signal_connect (connect_b, "button-press-event", G_CALLBACK(connect_cb), NULL);
+    gtk_grid_attach(GTK_GRID(grid),connect_b,0,row,1,1);
+
+    connect_addr_entry=gtk_entry_new();
+    gtk_entry_set_max_length(GTK_ENTRY(connect_addr_entry), 30);
+    gtk_grid_attach(GTK_GRID(grid),connect_addr_entry,1,row,1,1);
+    gtk_entry_set_text(GTK_ENTRY(connect_addr_entry), connect_addr);
+
+#endif
+    
 
     gtk_container_add (GTK_CONTAINER (content), grid);
     gtk_widget_show_all(discovery_dialog);
index 7155bb2202d600e06385a8c191d2f02c524e0f03..995ff72f977fad705afd5b0aa44dbae2ae1809c3 100644 (file)
@@ -116,6 +116,11 @@ static void display_waterfall_cb(GtkWidget *widget, gpointer data) {
   reconfigure_radio();
 }
 
+static void display_zoompan_cb(GtkWidget *widget, gpointer data) {
+  display_zoompan=display_zoompan==1?0:1;
+  reconfigure_radio();
+}
+
 static void display_sliders_cb(GtkWidget *widget, gpointer data) {
   display_sliders=display_sliders==1?0:1;
   reconfigure_radio();
@@ -395,7 +400,7 @@ void display_menu(GtkWidget *parent) {
   row++;
   row++;
   row++;
-  col=1;
+  col=0;
 
   
   GtkWidget *b_display_waterfall=gtk_check_button_new_with_label("Display Waterfall");
@@ -407,6 +412,14 @@ void display_menu(GtkWidget *parent) {
 
   col++;
 
+  GtkWidget *b_display_zoompan=gtk_check_button_new_with_label("Display Zoom/Pan");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_zoompan), display_zoompan);
+  gtk_widget_show(b_display_zoompan);
+  gtk_grid_attach(GTK_GRID(grid),b_display_zoompan,col,row,1,1);
+  g_signal_connect(b_display_zoompan,"toggled",G_CALLBACK(display_zoompan_cb),(gpointer *)NULL);
+
+  col++;
+
   GtkWidget *b_display_sliders=gtk_check_button_new_with_label("Display Sliders");
   //gtk_widget_override_font(b_display_sliders, pango_font_description_from_string("Arial 18"));
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_sliders), display_sliders);
index da4f775c356f13a42c4b9b5d0f42215fe4655137..bde18bfe37b58bc58aed7bcebc54951e16519113 100644 (file)
@@ -624,10 +624,10 @@ void encoder_menu(GtkWidget *parent) {
 
   GtkWidget *grid=gtk_grid_new();
 
-  gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE);
   gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
-  gtk_grid_set_column_spacing (GTK_GRID(grid),5);
-  gtk_grid_set_row_spacing (GTK_GRID(grid),5);
+  gtk_grid_set_column_spacing (GTK_GRID(grid),2);
+  gtk_grid_set_row_spacing (GTK_GRID(grid),2);
 
   GtkWidget *close_b=gtk_button_new_with_label("Close");
   g_signal_connect (close_b, "pressed", G_CALLBACK(close_cb), NULL);
diff --git a/ext.c b/ext.c
index 484be9d300a7205ce843cfeab02a49fcca1870cd..619271c1974a609d2fa2d7187beccd58b145af39 100644 (file)
--- a/ext.c
+++ b/ext.c
@@ -43,6 +43,7 @@
 #include "noise_menu.h"
 #include "wdsp.h"
 #include "ext.h"
+#include "zoompan.h"
 
 // The following calls functions can be called usig g_idle_add
 
@@ -139,7 +140,7 @@ int ext_noise_update(void *data) {
 }
 
 int ext_mox_update(void *data) {
-//g_print("ext_mox_update: %d\n",GPOINTER_TO_INT(data));
+g_print("ext_mox_update: %d\n",GPOINTER_TO_INT(data));
   mox_update(GPOINTER_TO_INT(data));
   return 0;
 }
@@ -645,34 +646,81 @@ int ext_set_duplex(void *data) {
   return 0;
 }
 
-int ext_set_rx_frequency(void *data) {
-  RX_FREQUENCY *p=(RX_FREQUENCY *)data;
-  int b = get_band_from_frequency(p->frequency);
-  if (b != vfo[p->rx].band) {
-    vfo_band_changed(b);
+int ext_remote_command(void *data) {
+  REMOTE_COMMAND *p=(REMOTE_COMMAND *)data;
+  switch(p->cmd) {
+    case RX_FREQ_CMD:
+      {
+        int b = get_band_from_frequency(p->data.frequency);
+        if (b != vfo[p->id].band) {
+          vfo_band_changed(b);
+        }
+        setFrequency(p->data.frequency);
+      }
+      break;
+    case RX_MOVE_CMD:
+      vfo_move(p->data.frequency,TRUE);
+      break;
+    case RX_MOVETO_CMD:
+      vfo_move_to(p->data.frequency);
+      break;
+    case RX_MODE_CMD:
+      vfo_mode_changed(p->data.mode);
+      break;
+    case RX_FILTER_CMD:
+      vfo_filter_changed(p->data.filter);
+      break;
+    case RX_AGC_CMD:
+      active_receiver->agc=p->data.agc;
+      set_agc(active_receiver, active_receiver->agc);
+      vfo_update();
+      break;
+    case RX_NR_CMD:
+      break;
+    case RX_NB_CMD:
+      break;
+    case RX_SNB_CMD:
+      break;
+    case RX_SPLIT_CMD:
+      break;
+    case RX_SAT_CMD:
+      break;
+    case RX_DUP_CMD:
+      break;
   }
-  setFrequency(p->frequency);
   g_free(data);
   return 0;
+}
 
+int ext_mute_update(void *data) {
+  active_receiver->mute_radio=!active_receiver->mute_radio;
   return 0;
 }
 
-int ext_set_rx_mode(void *data) {
-  RX_MODE *p=(RX_MODE *)data;
-  vfo_mode_changed(p->mode);
+int ext_zoom_update(void *data) {
+  update_zoom((double)GPOINTER_TO_INT(data));
   return 0;
 }
 
-int ext_set_rx_filter(void *data) {
-  RX_FILTER *p=(RX_FILTER *)data;
-  vfo_filter_changed(p->filter);
+int ext_zoom_set(void *data) {
+  int pos=GPOINTER_TO_INT(data);
+  double zoom=((double)pos/(100.0/7.0))+1.0;
+  if((int)zoom!=active_receiver->zoom) {
+    set_zoom(active_receiver->id,(double)zoom);
+  }
   return 0;
 }
 
-int ext_mute_update(void *data) {
-g_print("ext_mute_update: currently %d\n",active_receiver->mute_radio);
-  active_receiver->mute_radio=!active_receiver->mute_radio;
+int ext_pan_update(void *data) {
+  update_pan((double)GPOINTER_TO_INT(data));
   return 0;
 }
 
+int ext_pan_set(void *data) {
+  if(active_receiver->zoom>1) {
+    int pos=GPOINTER_TO_INT(data);
+    double pan=(double)((active_receiver->zoom-1)*active_receiver->width)*((double)pos/100.0);
+    set_pan(active_receiver->id,(double)pan);
+  }
+  return 0;
+}
diff --git a/ext.h b/ext.h
index f59aba7732f90d7e550e61029f5a82466939bb87..906a09931af8319de8517c6fbe124aceb70d149d 100644 (file)
--- a/ext.h
+++ b/ext.h
 // Use these calls from within the rigclt daemon, or the GPIO or MIDI stuff
 //
 
-typedef struct _RX_FREQUENCY {
-  int rx;
-  long long frequency;
-} RX_FREQUENCY;
-
-typedef struct _RX_MODE {
-  int rx;
-  int mode;
-} RX_MODE;
-
-typedef struct _RX_FILTER {
-  int rx;
-  int filter;
-} RX_FILTER;
+enum {
+  RX_FREQ_CMD,
+  RX_MOVE_CMD,
+  RX_MOVETO_CMD,
+  RX_MODE_CMD,
+  RX_FILTER_CMD,
+  RX_AGC_CMD,
+  RX_NR_CMD,
+  RX_NB_CMD,
+  RX_SNB_CMD,
+  RX_SPLIT_CMD,
+  RX_SAT_CMD,
+  RX_DUP_CMD
+};
+  
+typedef struct _REMOTE_COMMAND {
+  int id;
+  int cmd;
+  union {
+    long long frequency;
+    int mode;
+    int filter;
+    int agc;
+    int nr;
+    int nb;
+    int snb;
+    int split;
+    int sat;
+    int dup;
+  } data;
+} REMOTE_COMMAND;
 
 extern int ext_discovery(void *data);
 extern int ext_vfo_update(void *data);
@@ -130,7 +147,11 @@ int ext_update_noise(void *data);
 int ext_start_ps(void *data);
 #endif
 
-int ext_set_rx_frequency(void *data);
-int ext_set_rx_mode(void *data);
-int ext_set_rx_filter(void *data);
 int ext_mute_update(void *data);
+
+int ext_zoom_update(void *data);
+int ext_zoom_set(void *data);
+int ext_pan_update(void *data);
+int ext_pan_set(void *data);
+
+int ext_remote_command(void *data);
diff --git a/gpio.c b/gpio.c
index 57fd76afcb45a809165a3c70e51b5126775a0845..efd9e6442aefe1cd0b8565763b5afb69aafe610c 100644 (file)
--- a/gpio.c
+++ b/gpio.c
@@ -56,6 +56,7 @@
 #ifdef LOCALCW
 #include "iambic.h"
 #endif
+#include "zoompan.h"
 
 // debounce settle time in ms
 #define DEFAULT_SETTLE_TIME 50
@@ -131,6 +132,12 @@ int ENABLE_CW_BUTTONS=1;
 int CW_ACTIVE_LOW=1;
 #endif
 
+#ifdef PTT
+int ENABLE_PTT_GPIO=1;
+int PTT_GPIO=15;
+int PTT_ACTIVE_LOW=1;
+#endif
+
 int vfoEncoderPos;
 int vfoFunction;
 
@@ -190,87 +197,93 @@ static int running=0;
 char *encoder_string[ENCODER_ACTIONS] = {
   "NO ACTION",
   "AF GAIN",
-  "RF GAIN",
-  "AGC GAIN",
-  "IF WIDTH",
-  "IF SHIFT",
   "AF GAIN RX1",
-  "RF GAIN RX1",
   "AF GAIN RX2",
-  "RF GAIN RX2",
+  "AGC GAIN",
   "AGC GAIN RX1",
   "AGC GAIN RX2",
-  "IF WIDTH RX1",
-  "IF WIDTH RX2",
+  "ATTENUATION/RX GAIN",
+  "COMP",
+  "CW FREQUENCY",
+  "CW SPEED",
+  "DIVERSITY GAIN",
+  "DIVERSITY GAIN (coarse)",
+  "DIVERSITY GAIN (fine)",
+  "DIVERSITY PHASE",
+  "DIVERSITY PHASE (coarse)",
+  "DIVERSITY PHASE (fine)",
+  "DRIVE",
+  "IF SHIFT",
   "IF SHIFT RX1",
   "IF SHIFT RX2",
-  "ATTENUATION/RX GAIN",
+  "IF WIDTH",
+  "IF WIDTH RX1",
+  "IF WIDTH RX2",
   "MIC GAIN",
-  "DRIVE",
-  "TUNE DRIVE",
-  "RIT",
-  "RIT RX1",
-  "RIT RX2",
-  "XIT",
-  "CW SPEED",
-  "CW FREQUENCY",
+  "PAN",
   "PANADAPTER HIGH",
   "PANADAPTER LOW",
   "PANADAPTER STEP",
-  "WATERFALL HIGH",
-  "WATERFALL LOW",
+  "RF GAIN",
+  "RF GAIN RX1",
+  "RF GAIN RX2",
+  "RIT",
+  "RIT RX1",
+  "RIT RX2",
   "SQUELCH",
   "SQUELCH RX1",
   "SQUELCH RX2",
-  "COMP",
-  "DIVERSITY GAIN",
-  "DIVERSITY GAIN (coarse)",
-  "DIVERSITY GAIN (fine)",
-  "DIVERSITY PHASE",
-  "DIVERSITY PHASE (coarse)",
-  "DIVERSITY PHASE (fine)"
+  "TUNE DRIVE",
+  "WATERFALL HIGH",
+  "WATERFALL LOW",
+  "XIT",
+  "ZOOM",
 };
 
 char *sw_string[SWITCH_ACTIONS] = {
   "",
-  "TUNE",
+  "A TO B",
+  "A SWAP B",
+  "AGC",
+  "B TO A",
+  "BAND -",
+  "BAND +",
+  "BSTACK -",
+  "BSTACK +",
+  "CTUN",
+  "DIV",
+  "FILTER -",
+  "FILTER +",
+  "FUNCTION",
+  "LOCK",
+  "MENU BAND",
+  "MENU BSTACK",
+  "MENU DIV",
+  "MENU FILTER",
+  "MENU FREQUENCY",
+  "MENU MEMORY",
+  "MENU MODE",
+  "MENU PS",
+  "MODE -",
+  "MODE +",
   "MOX",
-  "PS",
-  "TWO TONE",
-  "NR",
+  "MUTE",
   "NB",
-  "SNB",
+  "NR",
+  "PAN -",
+  "PAN +",
+  "PS",
   "RIT",
   "RIT CL",
+  "SAT",
+  "SNB",
+  "SPLIT",
+  "TUNE",
+  "TWO TONE",
   "XIT",
   "XIT CL",
-  "BAND +",
-  "BAND -",
-  "BSTACK +",
-  "BSTACK -",
-  "MODE +",
-  "MODE -",
-  "FILTER +",
-  "FILTER -",
-  "A TO B",
-  "B TO A",
-  "A SWAP B",
-  "LOCK",
-  "CTUN",
-  "AGC",
-  "SPLIT",
-  "DIV",
-  "SAT",
-  "BAND",
-  "BSTACK",
-  "MODE",
-  "FILTER",
-  "FREQUENCY",
-  "MEMORY",
-  "DIV MENU",
-  "PS MENU",
-  "FUNCTION",
-  "MUTE"
+  "ZOOM -",
+  "ZOOM +",
 };
 
 int *sw_action=NULL;
@@ -365,6 +378,20 @@ static int vfo_function_released(void *data) {
   return 0;
 }
 
+#ifdef PTT
+static int ptt_pressed(void *data) {
+g_print("ptt_pressed\n");
+  if(can_transmit) g_idle_add(ext_mox_update,GINT_TO_POINTER(1));
+  return 0;
+}
+
+static int ptt_released(void *data) {
+g_print("ptt_released\n");
+  if(can_transmit) g_idle_add(ext_mox_update,GINT_TO_POINTER(0));
+  return 0;
+}
+#endif
+
 static int e_function_pressed(void *data) {
   int action=(int)data;
 g_print("e_function_pressed: %d\n",action);
@@ -487,6 +514,18 @@ g_print("e_function_pressed: %d\n",action);
     case MUTE:
       g_idle_add(ext_mute_update,NULL);
       break;
+    case PAN_MINUS:
+      g_idle_add(ext_pan_update,GINT_TO_POINTER(-100));
+      break;
+    case PAN_PLUS:
+      g_idle_add(ext_pan_update,GINT_TO_POINTER(100));
+      break;
+    case ZOOM_MINUS:
+      g_idle_add(ext_zoom_update,GINT_TO_POINTER(-1));
+      break;
+    case ZOOM_PLUS:
+      g_idle_add(ext_zoom_update,GINT_TO_POINTER(1));
+      break;
   }
   return 0;
 }
@@ -819,6 +858,28 @@ static void pI2CInterrupt() {
     }
 }
 
+#ifdef PTT
+static int ptt_level=1;
+static unsigned long ptt_debounce=0;
+
+static void pttAlert() {
+    int t=millis();
+    if(millis()<ptt_debounce) {
+      return;
+    }
+    int level=digitalRead(PTT_GPIO);
+    if(level!=ptt_level) {
+      if(level==0) {
+        if(running) g_idle_add(ptt_pressed,NULL);
+      } else {
+        if(running) g_idle_add(ptt_released,NULL);
+      }
+      ptt_level=level;
+      ptt_debounce=t+settle_time;
+    }
+}
+#endif
+
 void gpio_set_defaults(int ctrlr) {
 g_print("gpio_set_defaults: %d\n",ctrlr);
   if(sw_action!=NULL) {
@@ -1210,6 +1271,29 @@ void gpio_restore_state() {
   if(value) ENABLE_GPIO_SIDETONE=atoi(value);          
 #endif
 
+#ifdef PTT
+  switch(controller) {
+    case NO_CONTROLLER:
+      PTT_GPIO=12;
+      break;
+    case CONTROLLER1:
+      PTT_GPIO=12;
+      break;
+    case CONTROLLER2_V1:
+      PTT_GPIO=15;
+      break;
+    case CONTROLLER2_V2:
+      PTT_GPIO=15;
+      break;
+  }
+  value=getProperty("ENABLE_PTT_GPIO");                
+  if(value) ENABLE_PTT_GPIO=atoi(value);               
+  value=getProperty("PTT_GPIO");               
+  if(value) PTT_GPIO=atoi(value);              
+  value=getProperty("PTT_ACTIVE_LOW");         
+  if(value) PTT_ACTIVE_LOW=atoi(value);                
+#endif
+
   if(controller!=CONTROLLER1) {
     value=getProperty("i2c_device");
     if(value) {
@@ -1403,12 +1487,21 @@ void gpio_save_state() {
   setProperty("ENABLE_GPIO_SIDETONE",value);           
 #endif
 
+#ifdef PTT
+  sprintf(value,"%d",ENABLE_PTT_GPIO);         
+  setProperty("ENABLE_PPT_GPIO",value);                
+  sprintf(value,"%d",PTT_GPIO);                
+  setProperty("PPT_GPIO",value);               
+  sprintf(value,"%d",PTT_ACTIVE_LOW);          
+  setProperty("PPT_ACTIVE_LOW",value);         
+#endif
   saveProperties("gpio.props");
 
 }
 
 static void setup_pin(int pin, int up_down, void(*pAlert)(void)) {
   int rc;
+g_print("setup_pin: pin=%d up_down=%d\n",pin,up_down);
   pinMode(pin,GPIO);
   pinMode(pin,INPUT);
   pullUpDnControl(pin,up_down);
@@ -1642,6 +1735,13 @@ int gpio_init() {
   }
 #endif
 
+#ifdef PTT
+  if(ENABLE_PTT_GPIO) {
+g_print("PTT Enabled: setup pin %d active_low=%d\n",PTT_GPIO,PTT_ACTIVE_LOW);
+    setup_pin(PTT_GPIO,PTT_ACTIVE_LOW?PUD_UP:PUD_DOWN,pttAlert);
+  }
+#endif
+
   return 0;
 }
 
@@ -2079,6 +2179,12 @@ static void encoder_changed(int action,int pos) {
     case ENCODER_DIVERSITY_PHASE_FINE:
       update_diversity_phase((double)pos*0.1);
       break;
+    case ENCODER_ZOOM:
+      update_zoom((double)pos);
+      break;
+    case ENCODER_PAN:
+      update_pan((double)pos*100);
+      break;
   }
 }
 
diff --git a/gpio.h b/gpio.h
index e99995065430ba801262c96243d45b9588d8dd0d..ad204b8883e7f0572b26ecfe4b0dc262949778b7 100644 (file)
--- a/gpio.h
+++ b/gpio.h
@@ -25,7 +25,6 @@ enum {
   CONTROLLER1,
   CONTROLLER2_V1,
   CONTROLLER2_V2,
-  CUSTOM_CONTROLLER
 };
 
 extern int controller;
@@ -33,45 +32,47 @@ extern int controller;
 enum {
   ENCODER_NO_ACTION=0,
   ENCODER_AF_GAIN,
-  ENCODER_RF_GAIN,
-  ENCODER_AGC_GAIN,
-  ENCODER_IF_WIDTH,
-  ENCODER_IF_SHIFT,
   ENCODER_AF_GAIN_RX1,
-  ENCODER_RF_GAIN_RX1,
   ENCODER_AF_GAIN_RX2,
-  ENCODER_RF_GAIN_RX2,
+  ENCODER_AGC_GAIN,
   ENCODER_AGC_GAIN_RX1,
   ENCODER_AGC_GAIN_RX2,
-  ENCODER_IF_WIDTH_RX1,
-  ENCODER_IF_WIDTH_RX2,
+  ENCODER_ATTENUATION,
+  ENCODER_COMP,
+  ENCODER_CW_FREQUENCY,
+  ENCODER_CW_SPEED,
+  ENCODER_DIVERSITY_GAIN,
+  ENCODER_DIVERSITY_GAIN_COARSE,
+  ENCODER_DIVERSITY_GAIN_FINE,
+  ENCODER_DIVERSITY_PHASE,
+  ENCODER_DIVERSITY_PHASE_COARSE,
+  ENCODER_DIVERSITY_PHASE_FINE,
+  ENCODER_DRIVE,
+  ENCODER_IF_SHIFT,
   ENCODER_IF_SHIFT_RX1,
   ENCODER_IF_SHIFT_RX2,
-  ENCODER_ATTENUATION,
+  ENCODER_IF_WIDTH,
+  ENCODER_IF_WIDTH_RX1,
+  ENCODER_IF_WIDTH_RX2,
   ENCODER_MIC_GAIN,
-  ENCODER_DRIVE,
-  ENCODER_TUNE_DRIVE,
-  ENCODER_RIT,
-  ENCODER_RIT_RX1,
-  ENCODER_RIT_RX2,
-  ENCODER_XIT,
-  ENCODER_CW_SPEED,
-  ENCODER_CW_FREQUENCY,
+  ENCODER_PAN,
   ENCODER_PANADAPTER_HIGH,
   ENCODER_PANADAPTER_LOW,
   ENCODER_PANADAPTER_STEP,
-  ENCODER_WATERFALL_HIGH,
-  ENCODER_WATERFALL_LOW,
+  ENCODER_RF_GAIN,
+  ENCODER_RF_GAIN_RX1,
+  ENCODER_RF_GAIN_RX2,
+  ENCODER_RIT,
+  ENCODER_RIT_RX1,
+  ENCODER_RIT_RX2,
   ENCODER_SQUELCH,
   ENCODER_SQUELCH_RX1,
   ENCODER_SQUELCH_RX2,
-  ENCODER_COMP,
-  ENCODER_DIVERSITY_GAIN,
-  ENCODER_DIVERSITY_GAIN_COARSE,
-  ENCODER_DIVERSITY_GAIN_FINE,
-  ENCODER_DIVERSITY_PHASE,
-  ENCODER_DIVERSITY_PHASE_COARSE,
-  ENCODER_DIVERSITY_PHASE_FINE,
+  ENCODER_TUNE_DRIVE,
+  ENCODER_WATERFALL_HIGH,
+  ENCODER_WATERFALL_LOW,
+  ENCODER_XIT,
+  ENCODER_ZOOM,
   ENCODER_ACTIONS
 };
 
@@ -79,44 +80,48 @@ extern char *encoder_string[ENCODER_ACTIONS];
 
 enum {
   NO_ACTION=0,
-  TUNE,
-  MOX,
-  PS,
-  TWO_TONE,
-  NR,
-  NB,
-  SNB,
-  RIT,
-  RIT_CLEAR,
-  XIT,
-  XIT_CLEAR,
-  BAND_PLUS,
-  BAND_MINUS,
-  BANDSTACK_PLUS,
-  BANDSTACK_MINUS,
-  MODE_PLUS,
-  MODE_MINUS,
-  FILTER_PLUS,
-  FILTER_MINUS,
   A_TO_B,
-  B_TO_A,
   A_SWAP_B,
-  LOCK,
-  CTUN,
   AGC,
-  SPLIT,
+  B_TO_A,
+  BAND_MINUS,
+  BAND_PLUS,
+  BANDSTACK_MINUS,
+  BANDSTACK_PLUS,
+  CTUN,
   DIVERSITY,
-  SAT,
+  FILTER_MINUS,
+  FILTER_PLUS,
+  FUNCTION,
+  LOCK,
   MENU_BAND,
   MENU_BANDSTACK,
-  MENU_MODE,
+  MENU_DIVERSITY,
   MENU_FILTER,
   MENU_FREQUENCY,
   MENU_MEMORY,
-  MENU_DIVERSITY,
+  MENU_MODE,
   MENU_PS,
-  FUNCTION,
+  MODE_MINUS,
+  MODE_PLUS,
+  MOX,
   MUTE,
+  NB,
+  NR,
+  PAN_MINUS,
+  PAN_PLUS,
+  PS,
+  RIT,
+  RIT_CLEAR,
+  SAT,
+  SNB,
+  SPLIT,
+  TUNE,
+  TWO_TONE,
+  XIT,
+  XIT_CLEAR,
+  ZOOM_MINUS,
+  ZOOM_PLUS,
   SWITCH_ACTIONS
 };
 
@@ -239,6 +244,12 @@ extern int  gpio_right_cw_key();
 extern int  gpio_cw_sidetone_enabled();
 #endif
 
+#ifdef PTT
+extern int ENABLE_PTT_GPIO;
+extern int PTT_GPIO;
+extern int PTT_ACTIVE_LOW;
+#endif
+
 extern void gpio_set_defaults(int ctrlr);
 extern void gpio_restore_actions();
 extern void gpio_restore_state();
diff --git a/i2c.c b/i2c.c
index 83724083c8b371bffee8fd3bf25d1a2b7f244dd0..b6863474222c02d66e57c179cd59cc83345877c6 100644 (file)
--- a/i2c.c
+++ b/i2c.c
@@ -90,58 +90,6 @@ void i2c_interrupt() {
         for(i=0;i<16;i++) {
           if(i2c_sw[i]==ints) break;
         }
-/*
-        switch(ints) {
-          case SW_2:
-            i=CONTROLLER2_SW2;
-            break;
-          case SW_3:
-            i=CONTROLLER2_SW3;
-            break;
-          case SW_4:
-            i=CONTROLLER2_SW4;
-            break;
-          case SW_5:
-            i=CONTROLLER2_SW5;
-            break;
-          case SW_6:
-            i=CONTROLLER2_SW6;
-            break;
-          case SW_7:
-            i=CONTROLLER2_SW7;
-            break;
-          case SW_8:
-            i=CONTROLLER2_SW8;
-            break;
-          case SW_9:
-            i=CONTROLLER2_SW9;
-            break;
-          case SW_10:
-            i=CONTROLLER2_SW10;
-            break;
-          case SW_11:
-            i=CONTROLLER2_SW11;
-            break;
-          case SW_12:
-            i=CONTROLLER2_SW12;
-            break;
-          case SW_13:
-            i=CONTROLLER2_SW13;
-            break;
-          case SW_14:
-            i=CONTROLLER2_SW14;
-            break;
-          case SW_15:
-            i=CONTROLLER2_SW15;
-            break;
-          case SW_16:
-            i=CONTROLLER2_SW16;
-            break;
-          case SW_17:
-            i=CONTROLLER2_SW17;
-            break;
-        }
-*/
         if(i<16) {
 //g_print("i1c_interrupt: sw=%d action=%d\n",i,sw_action[i]);
           switch(sw_action[i]) {
@@ -271,6 +219,18 @@ void i2c_interrupt() {
             case MUTE:
               g_idle_add(ext_mute_update,NULL);
               break;
+            case PAN_MINUS:
+              g_idle_add(ext_pan_update,GINT_TO_POINTER(-100));
+              break;
+            case PAN_PLUS:
+              g_idle_add(ext_pan_update,GINT_TO_POINTER(100));
+              break;
+            case ZOOM_MINUS:
+              g_idle_add(ext_zoom_update,GINT_TO_POINTER(-1));
+              break;
+            case ZOOM_PLUS:
+              g_idle_add(ext_zoom_update,GINT_TO_POINTER(1));
+              break;
           }
         }
       }
diff --git a/midi.h b/midi.h
index 4c5076669bc802dbcee04bbae6849edf73bddd41..fbfa310f53a2953cf38b0649b6144eb33fc8b498 100644 (file)
--- a/midi.h
+++ b/midi.h
@@ -49,7 +49,7 @@
 enum MIDIaction {
   ACTION_NONE=0,       // NONE:                No-Op (unassigned key)
   VFO_A2B,             // A2B:                 VFO A -> B
-  AF_GAIN,             // AFGAIN:              AF gain
+  MIDI_AF_GAIN,                // AFGAIN:              AF gain
   AGCATTACK,           // AGCATTACK:           AGC ATTACK (cycle fast/med/slow etc.)
   MIDI_AGC,            // AGCVAL:              AGC level
   ATT,                 // ATT:                 Step attenuator or Programmable attenuator
@@ -80,6 +80,7 @@ enum MIDIaction {
   MIDI_MUTE,           // MUTE:                toggle mute on/off
   MIDI_NB,             // NOISEBLANKER:        cycle through NoiseBlanker states (none, NB, NB2)
   MIDI_NR,             // NOISEREDUCTION:      cycle through NoiseReduction states (none, NR, NR2)
+  MIDI_PAN,            // PAN:                 change panning of panadater/waterfall when zoomed
   PAN_HIGH,            // PANHIGH:             "high" value of current panadapter
   PAN_LOW,             // PANLOW:              "low" value of current panadapter
   PRE,                 // PREAMP:              preamp on/off
@@ -103,6 +104,9 @@ enum MIDIaction {
   VOXLEVEL,            // VOXLEVEL:            adjust VOX threshold
   MIDI_XIT_CLEAR,      // XITCLEAR:            clear XIT value
   XIT_VAL,             // XITVAL:              change XIT value
+  MIDI_ZOOM,           // ZOOM:                change zoom factor
+  ZOOM_UP,             // ZOOMUP:              change zoom factor
+  ZOOM_DOWN,           // ZOOMDOWN:            change zoom factor
 };
 
 //
diff --git a/midi2.c b/midi2.c
index b152b3a48ed0f0d7271e2c2674303836e581c20d..94b23e9a5ef478984221e9cd4960b72a74655da0 100644 (file)
--- a/midi2.c
+++ b/midi2.c
@@ -102,7 +102,7 @@ static struct {
   const char *str;
 } ActionTable[] = {
        { VFO_A2B,              "A2B"},
-        { AF_GAIN,             "AFGAIN"},
+        { MIDI_AF_GAIN,        "AFGAIN"},
        { AGCATTACK,            "AGCATTACK"},
         { MIDI_AGC,            "AGCVAL"},
         { ATT,                 "ATT"},
@@ -133,6 +133,7 @@ static struct {
        { MIDI_MUTE,            "MUTE"},
        { MIDI_NB,              "NOISEBLANKER"},
        { MIDI_NR,              "NOISEREDUCTION"},
+        { MIDI_PAN,            "PAN"},
         { PAN_HIGH,            "PANHIGH"},
         { PAN_LOW,             "PANLOW"},
         { PRE,                 "PREAMP"},
@@ -156,6 +157,9 @@ static struct {
        { VOXLEVEL,             "VOXLEVEL"},
        { MIDI_XIT_CLEAR,       "XITCLEAR"},
        { XIT_VAL,              "XITVAL"},
+       { MIDI_ZOOM,            "ZOOM"},
+       { ZOOM_UP,              "ZOOMUP"},
+       { ZOOM_DOWN,            "ZOOMDOWN"},
         { ACTION_NONE,         "NONE"}
 };
 
diff --git a/midi3.c b/midi3.c
index 776c1ce503c06fe1e9b1180c28fd3ca5ce0e3faa..ff92e315ab079a234b6f8ae17c52830374e81182 100644 (file)
--- a/midi3.c
+++ b/midi3.c
@@ -44,7 +44,7 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
            }
            break;
        /////////////////////////////////////////////////////////// "AFGAIN"
-       case AF_GAIN: // knob or wheel supported
+       case MIDI_AF_GAIN: // knob or wheel supported
             switch (type) {
              case MIDI_KNOB:
                active_receiver->volume = 0.01*val;
@@ -470,6 +470,17 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
              g_idle_add(ext_vfo_update, NULL);
            }
            break;
+       /////////////////////////////////////////////////////////// "PAN"
+        case MIDI_PAN:  // wheel and knob
+           switch (type) {
+              case MIDI_WHEEL:
+                g_idle_add(ext_pan_update,GINT_TO_POINTER(val));
+                break;
+             case MIDI_KNOB:
+                g_idle_add(ext_pan_set,GINT_TO_POINTER(val));
+                break;
+            }
+            break;
        /////////////////////////////////////////////////////////// "PANHIGH"
        case PAN_HIGH:  // wheel or knob
            switch (type) {
@@ -806,6 +817,38 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
               g_idle_add(ext_vfo_update, NULL);
            }
             break;
+       /////////////////////////////////////////////////////////// "ZOOM"
+        case MIDI_ZOOM:  // wheel and knob
+            switch (type) {
+              case MIDI_WHEEL:
+g_print("MIDI_ZOOM: MIDI_WHEEL: val=%d\n",val);
+                g_idle_add(ext_zoom_update,GINT_TO_POINTER(val));
+                break;
+              case MIDI_KNOB:
+g_print("MIDI_ZOOM: MIDI_KNOB: val=%d\n",val);
+                g_idle_add(ext_zoom_set,GINT_TO_POINTER(val));
+                break;
+            }
+            break;
+       /////////////////////////////////////////////////////////// "ZOOMDOWN"
+       /////////////////////////////////////////////////////////// "ZOOMUP"
+        case ZOOM_UP:  // key
+        case ZOOM_DOWN:  // key
+           switch (type) {
+             case MIDI_KEY:
+               new =  (action == ZOOM_UP) ? 1 : -1;
+                g_idle_add(ext_zoom_update,GINT_TO_POINTER(new));
+               break;
+             case MIDI_WHEEL:
+               new = (val > 0) ? 1 : -1;
+                g_idle_add(ext_zoom_update,GINT_TO_POINTER(new));
+               break;
+             default:
+               // do nothing
+               // we should not come here anyway
+               break;
+           }
+            break;
 
        case ACTION_NONE:
            // No error message, this is the "official" action for un-used controller buttons.
index 979ff064db4032978526583acaf7bf80e1f2b681..3aef1bd578ee3cf641f8e1d561367d6394c9ee70 100644 (file)
@@ -1600,6 +1600,7 @@ static gpointer iq_thread(gpointer data) {
     if(ddc_sequence[ddc] !=sequence) {
       g_print("DDC %d sequence error: expected %ld got %ld\n",ddc,ddc_sequence[ddc],sequence);
       ddc_sequence[ddc]=sequence;
+      sequence_errors++;
     }
     ddc_sequence[ddc]++;
 //
index 82fc028b5feeb3d900ce1e1e36f13a2704866193..b08120d8b7f744643020c2d5b3613cb9fd059453 100644 (file)
@@ -532,6 +532,7 @@ static gpointer receive_thread(gpointer arg) {
              // and is no error condition
               if (sequence != 0 && sequence != last_seq_num+1) {
                g_print("SEQ ERROR: last %ld, recvd %ld\n", (long) last_seq_num, (long) sequence);
+                sequence_errors++;
              }
              last_seq_num=sequence;
               switch(ep) {
diff --git a/radio.c b/radio.c
index 893c30b99d4308f2af37eee7ab751f7e8e920360..81a7310c5cda1db78a9277f51fee6de0045ab58f 100644 (file)
--- a/radio.c
+++ b/radio.c
@@ -60,6 +60,7 @@
 #include "rx_panadapter.h"
 #include "tx_panadapter.h"
 #include "waterfall.h"
+#include "zoompan.h"
 #include "sliders.h"
 #include "toolbar.h"
 #include "rigctl.h"
@@ -70,6 +71,9 @@
 #ifdef MIDI
 #include "midi.h"
 #endif
+#ifdef SERVER
+#include "hpsdr_server.h"
+#endif
 
 #define min(x,y) (x<y?x:y)
 #define max(x,y) (x<y?y:x)
@@ -81,6 +85,7 @@
 #define METER_HEIGHT (60)
 #define METER_WIDTH (200)
 #define PANADAPTER_HEIGHT (105)
+#define ZOOMPAN_HEIGHT (50)
 #define SLIDERS_HEIGHT (100)
 #define TOOLBAR_HEIGHT (30)
 #define WATERFALL_HEIGHT (105)
@@ -89,6 +94,7 @@ GtkWidget *fixed;
 static GtkWidget *vfo_panel;
 static GtkWidget *meter;
 static GtkWidget *menu;
+static GtkWidget *zoompan;
 static GtkWidget *sliders;
 static GtkWidget *toolbar;
 static GtkWidget *panadapter;
@@ -161,6 +167,7 @@ double display_average_time=120.0;
 int waterfall_high=-100;
 int waterfall_low=-150;
 
+int display_zoompan=0;
 int display_sliders=0;
 int display_toolbar=0;
 
@@ -304,6 +311,9 @@ int can_transmit=0;
 
 gboolean duplex=FALSE;
 gboolean mute_rx_while_transmitting=FALSE;
+
+int sequence_errors=0;
+
 gint rx_height;
 
 void radio_stop() {
@@ -324,6 +334,9 @@ void reconfigure_radio() {
   int y;
 //g_print("reconfigure_radio: receivers=%d\n",receivers);
   rx_height=display_height-VFO_HEIGHT;
+  if(display_zoompan) {
+    rx_height-=ZOOMPAN_HEIGHT;
+  }
   if(display_sliders) {
     rx_height-=SLIDERS_HEIGHT;
   }
@@ -340,6 +353,22 @@ void reconfigure_radio() {
     y+=rx_height/receivers;
   }
 
+  if(display_zoompan) {
+    if(zoompan==NULL) {
+      zoompan = zoompan_init(display_width,ZOOMPAN_HEIGHT);
+      gtk_fixed_put(GTK_FIXED(fixed),zoompan,0,y);
+    } else {
+      gtk_fixed_put(GTK_FIXED(fixed),zoompan,0,y);
+    }
+    gtk_widget_show_all(zoompan);
+    y+=ZOOMPAN_HEIGHT;
+  } else {
+    if(zoompan!=NULL) {
+      gtk_container_remove(GTK_CONTAINER(fixed),zoompan); 
+      zoompan=NULL;
+    }
+  }
+
   if(display_sliders) {
     if(sliders==NULL) {
       sliders = sliders_init(display_width,SLIDERS_HEIGHT);
@@ -880,15 +909,18 @@ void start_radio() {
   switch(controller) {
     case CONTROLLER2_V1:
     case CONTROLLER2_V2:
+      display_zoompan=1;
       display_sliders=0;
       display_toolbar=0;
       break;
     default:
+      display_zoompan=1;
       display_sliders=1;
       display_toolbar=1;
       break;
   }
 #else
+  display_zoompan=1;
   display_sliders=1;
   display_toolbar=1;
 #endif
@@ -957,6 +989,9 @@ void start_radio() {
 
 
   rx_height=display_height-VFO_HEIGHT;
+  if(display_zoompan) {
+    rx_height-=ZOOMPAN_HEIGHT;
+  }
   if(display_sliders) {
     rx_height-=SLIDERS_HEIGHT;
   }
@@ -997,6 +1032,7 @@ void start_radio() {
       transmitter=create_transmitter(CHANNEL_TX, buffer_size, fft_size, updates_per_second, display_width/4, display_height/2);
     } else {
       int tx_height=display_height-VFO_HEIGHT;
+      if(display_zoompan) tx_height-=ZOOMPAN_HEIGHT;
       if(display_sliders) tx_height-=SLIDERS_HEIGHT;
       if(display_toolbar) tx_height-=TOOLBAR_HEIGHT;
       transmitter=create_transmitter(CHANNEL_TX, buffer_size, fft_size, updates_per_second, display_width, tx_height);
@@ -1012,15 +1048,15 @@ void start_radio() {
     receiver[PS_RX_FEEDBACK]=create_pure_signal_receiver(PS_RX_FEEDBACK, buffer_size,protocol==ORIGINAL_PROTOCOL?active_receiver->sample_rate:192000,display_width);
     switch (protocol) {
       case NEW_PROTOCOL:
-       pk = 0.2899;
-       break;
+        pk = 0.2899;
+        break;
       case ORIGINAL_PROTOCOL:
         switch (device) {
-         case DEVICE_HERMES_LITE2:
-           pk = 0.2300;
+          case DEVICE_HERMES_LITE2:
+            pk = 0.2300;
             break;
-         default:
-           pk = 0.4067;
+          default:
+            pk = 0.4067;
             break;
         }
     }
@@ -1033,12 +1069,26 @@ void start_radio() {
   audio_waterfall=audio_waterfall_init(200,100);
   gtk_fixed_put(GTK_FIXED(fixed),audio_waterfall,0,VFO_HEIGHT+20);
 #endif
-  
+
+  gboolean init_gpio=FALSE;
+#ifdef LOCALCW
+  init_gpio=TRUE;
+#endif
+#ifdef PTT
+  init_gpio=TRUE;
+#endif
+#ifdef GPIO
+  init_gpio=TRUE;
+#endif
+
+  if(init_gpio) {
 #ifdef GPIO
     if(gpio_init()<0) {
       g_print("GPIO failed to initialize\n");
     }
 #endif
+  }
+
 #ifdef LOCALCW
   // init local keyer if enabled
   if (cw_keyer_internal == 0) {
@@ -1061,6 +1111,12 @@ void start_radio() {
 #endif
   }
 
+  if(display_zoompan) {
+    zoompan = zoompan_init(display_width,ZOOMPAN_HEIGHT);
+    gtk_fixed_put(GTK_FIXED(fixed),zoompan,0,y);
+    y+=ZOOMPAN_HEIGHT;
+  }
+
   if(display_sliders) {
 //g_print("create sliders\n");
     sliders = sliders_init(display_width,SLIDERS_HEIGHT);
@@ -1162,6 +1218,10 @@ void start_radio() {
   MIDIstartup();
 #endif
 
+#ifdef SERVER
+  create_hpsdr_server();
+#endif
+
 }
 
 void disable_rigctl() {
@@ -1227,7 +1287,7 @@ void radio_change_sample_rate(int rate) {
       break;
 #ifdef SOAPYSDR
     case SOAPYSDR_PROTOCOL:
-      soapy_protocol_change_sample_rate(receiver[0],rate);
+      soapy_protocol_change_sample_rate(receiver[0]);
       soapy_protocol_set_mic_sample_rate(rate);
       break;
 #endif
@@ -1755,6 +1815,8 @@ g_print("radioRestoreState: %s\n",property_path);
     if(value) panadapter_high=atoi(value);
     value=getProperty("panadapter_low");
     if(value) panadapter_low=atoi(value);
+    value=getProperty("display_zoompan");
+    if(value) display_zoompan=atoi(value);
     value=getProperty("display_sliders");
     if(value) display_sliders=atoi(value);
     value=getProperty("display_toolbar");
@@ -2005,6 +2067,8 @@ g_print("radioSaveState: %s\n",property_path);
     setProperty("panadapter_high",value);
     sprintf(value,"%d",panadapter_low);
     setProperty("panadapter_low",value);
+    sprintf(value,"%d",display_zoompan);
+    setProperty("display_zoompan",value);
     sprintf(value,"%d",display_sliders);
     setProperty("display_sliders",value);
     sprintf(value,"%d",display_toolbar);
diff --git a/radio.h b/radio.h
index 818d991946644ba0ba4b0707fe9b9810f675e094..53aa27aaf71fd2cfff6d197af2156e0889ccca3b 100644 (file)
--- a/radio.h
+++ b/radio.h
@@ -151,6 +151,7 @@ extern double display_average_time;
 
 //extern int display_waterfall;
 
+extern int display_zoompan;
 extern int display_sliders;
 extern int display_toolbar;
 
@@ -292,6 +293,8 @@ extern int have_rx_gain;   // TRUE on HermesLite/RadioBerry
 extern int rx_gain_calibration;  // position of the RX gain slider that
                                 // corresponds to zero amplification/attenuation
 
+extern int sequence_errors;
+
 extern void radio_stop();
 extern void reconfigure_radio();
 extern void start_radio();
index 73bc1521b834ed40fdc0b129c82b930a0713fa67..107174a67c4c60e2848f446cfb98943bd9ef3bf3 100644 (file)
@@ -41,6 +41,7 @@
 #include "vfo.h"
 #include "meter.h"
 #include "rx_panadapter.h"
+#include "zoompan.h"
 #include "sliders.h"
 #include "waterfall.h"
 #include "new_protocol.h"
@@ -91,15 +92,14 @@ gboolean receiver_button_release_event(GtkWidget *widget, GdkEventButton *event,
     making_active=FALSE;
     g_idle_add(menu_active_receiver_changed,NULL);
     g_idle_add(ext_vfo_update,NULL);
+    g_idle_add(zoompan_active_receiver_changed,NULL);
     g_idle_add(sliders_active_receiver_changed,NULL);
     if(event->button==3) {
       g_idle_add(ext_start_rx,NULL);
     }
 
-    g_print("receiver: %d adc=%d attenuation=%d rx_gain_calibration=%d\n",rx->id,rx->adc,adc_attenuation[rx->adc],rx_gain_calibration);
+    ///g_print("receiver: %d adc=%d attenuation=%d rx_gain_calibration=%d\n",rx->id,rx->adc,adc_attenuation[rx->adc],rx_gain_calibration);
   } else {
-    //int display_width=gtk_widget_get_allocated_width (rx->panadapter);
-    //int display_height=gtk_widget_get_allocated_height (rx->panadapter);
     if(pressed) {
       int x=(int)event->x;
       if (event->button == 1) {
@@ -308,6 +308,12 @@ void receiver_save_state(RECEIVER *rx) {
   sprintf(value,"%f",rx->squelch);
   setProperty(name,value);
 
+  sprintf(name,"receiver.%d.zoom",rx->id);
+  sprintf(value,"%d",rx->zoom);
+  setProperty(name,value);
+  sprintf(name,"receiver.%d.pan",rx->id);
+  sprintf(value,"%d",rx->pan);
+  setProperty(name,value);
 }
 
 void receiver_restore_state(RECEIVER *rx) {
@@ -488,6 +494,12 @@ fprintf(stderr,"receiver_restore_state: id=%d\n",rx->id);
   value=getProperty(name);
   if(value) rx->squelch=atof(value);
 
+  sprintf(name,"receiver.%d.zoom",rx->id);
+  value=getProperty(name);
+  if(value) rx->zoom=atoi(value);
+  sprintf(name,"receiver.%d.pan",rx->id);
+  value=getProperty(name);
+  if(value) rx->pan=atoi(value);
 }
 
 void reconfigure_receiver(RECEIVER *rx,int height) {
@@ -552,21 +564,23 @@ static gint update_display(gpointer data) {
 //fprintf(stderr,"update_display: %d displaying=%d\n",rx->id,rx->displaying);
 
   if(rx->displaying) {
-    GetPixels(rx->id,0,rx->pixel_samples,&rc);
-    if(rc) {
-      if(rx->display_panadapter) {
-        rx_panadapter_update(rx);
-      }
-      if(rx->display_waterfall) {
-        waterfall_update(rx);
-      }
+    if(rx->pixels>0) {
+      GetPixels(rx->id,0,rx->pixel_samples,&rc);
+      if(rc) {
+        if(rx->display_panadapter) {
+          rx_panadapter_update(rx);
+        }
+        if(rx->display_waterfall) {
+          waterfall_update(rx);
+        }
     }
-
-    if(active_receiver==rx) {
-      double m=GetRXAMeter(rx->id,smeter)+meter_calibration;
-      meter_update(rx,SMETER,m,0.0,0.0,0.0);
+  
+      if(active_receiver==rx) {
+        double m=GetRXAMeter(rx->id,smeter)+meter_calibration;
+        meter_update(rx,SMETER,m,0.0,0.0,0.0);
+      }
+      return TRUE;
     }
-    return TRUE;
   }
   return FALSE;
 }
@@ -677,7 +691,7 @@ static void init_analyzer(RECEIVER *rx) {
 
     overlap = (int)max(0.0, ceil(fft_size - (double)rx->sample_rate / (double)rx->fps));
 
-    fprintf(stderr,"SetAnalyzer id=%d buffer_size=%d overlap=%d\n",rx->id,rx->buffer_size,overlap);
+    //g_print("SetAnalyzer id=%d buffer_size=%d overlap=%d\n",rx->id,rx->buffer_size,overlap);
 
 
     SetAnalyzer(rx->id,
@@ -893,17 +907,9 @@ fprintf(stderr,"create_receiver: id=%d buffer_size=%d fft_size=%d pixels=%d fps=
 fprintf(stderr,"create_receiver: id=%d default adc=%d\n",rx->id, rx->adc);
 #ifdef SOAPYSDR
   if(radio->device==SOAPYSDR_USB_DEVICE) {
-/*
-    if(strcmp(radio->name,"lime")==0) {
-      rx->sample_rate=384000;
-    } else if(strcmp(radio->name,"rtlsdr")==0) {
-      rx->sample_rate=384000;
-    } else {
-      rx->sample_rate=384000;
-    }
-*/
     rx->sample_rate=radio->info.soapy.sample_rate;
-    rx->resample_step=1;
+    rx->resampler=NULL;
+    rx->resample_buffer=NULL;
   } else {
 #endif
     rx->sample_rate=48000;
@@ -912,23 +918,12 @@ fprintf(stderr,"create_receiver: id=%d default adc=%d\n",rx->id, rx->adc);
 #endif
   rx->buffer_size=buffer_size;
   rx->fft_size=fft_size;
-  rx->pixels=pixels;
   rx->fps=fps;
   rx->update_timer_id=-1;
 
-
-//  rx->dds_offset=0;
-//  rx->rit=0;
-
   rx->width=width;
   rx->height=height;
 
-  // allocate buffers
-  rx->iq_input_buffer=g_new(double,2*rx->buffer_size);
-  rx->audio_buffer_size=480;
-  rx->audio_sequence=0L;
-  rx->pixel_samples=g_new(float,pixels);
-
   rx->samples=0;
   rx->displaying=0;
   rx->display_panadapter=1;
@@ -992,13 +987,20 @@ fprintf(stderr,"create_receiver: id=%d default adc=%d\n",rx->id, rx->adc);
 
   rx->mute_radio=0;
 
+  rx->fexchange_errors=0;
+
+  rx->zoom=1;
+  rx->pan=0;
+
   receiver_restore_state(rx);
 
-#ifdef SOAPYSDR
-  rx->resample_step=radio->info.soapy.sample_rate/rx->sample_rate;
-#else
-  rx->resample_step=1;
-#endif
+  // allocate buffers
+  rx->iq_input_buffer=g_new(double,2*rx->buffer_size);
+  rx->audio_buffer_size=480;
+  rx->audio_sequence=0L;
+  rx->pixels=pixels*rx->zoom;
+  rx->pixel_samples=g_new(float,rx->pixels);
+
 
 fprintf(stderr,"create_receiver (after restore): rx=%p id=%d audio_buffer_size=%d local_audio=%d\n",rx,rx->id,rx->audio_buffer_size,rx->local_audio);
   //rx->audio_buffer=g_new(guchar,rx->audio_buffer_size);
@@ -1008,7 +1010,7 @@ fprintf(stderr,"create_receiver (after restore): rx=%p id=%d audio_buffer_size=%
 
 fprintf(stderr,"create_receiver: id=%d output_samples=%d\n",rx->id,rx->output_samples);
 
-  rx->hz_per_pixel=(double)rx->sample_rate/(double)rx->width;
+  rx->hz_per_pixel=(double)rx->sample_rate/(double)rx->pixels;
 
   // setup wdsp for this receiver
 
@@ -1069,7 +1071,6 @@ fprintf(stderr,"RXASetMP %d\n",rx->low_latency);
   }
 
   receiver_mode_changed(rx);
-  //receiver_frequency_changed(rx);
 
   int result;
   XCreateAnalyzer(rx->id, &result, 262144, 1, 1, "");
@@ -1143,13 +1144,10 @@ g_print("receiver_change_sample_rate: id=%d rate=%d scale=%d buffer_size=%d outp
   SetInputSamplerate(rx->id, sample_rate);
   SetEXTANBSamplerate (rx->id, sample_rate);
   SetEXTNOBSamplerate (rx->id, sample_rate);
-#ifdef SOAPYSDR
   if(protocol==SOAPYSDR_PROTOCOL) {
-    rx->resample_step=radio_sample_rate/rx->sample_rate;
-g_print("receiver_change_sample_rate: resample_step=%d\n",rx->resample_step);
+    soapy_protocol_change_sample_rate(rx);
     soapy_protocol_set_mic_sample_rate(rx->sample_rate);
   }
-#endif
 
   SetChannelState(rx->id,1,0);
 
@@ -1162,9 +1160,29 @@ void receiver_frequency_changed(RECEIVER *rx) {
   int id=rx->id;
 
   if(vfo[id].ctun) {
+
+    long long frequency=vfo[id].frequency;
+    long long half=(long long)rx->sample_rate/2LL;
+    long long rx_low=vfo[id].ctun_frequency+rx->filter_low;
+    long long rx_high=vfo[id].ctun_frequency+rx->filter_high;
+
+    if(rx->zoom>1) {
+      long long min_display=frequency-half+(long long)((double)rx->pan*rx->hz_per_pixel);
+      long long max_display=min_display+(long long)((double)rx->width*rx->hz_per_pixel);
+      if(rx_low<=min_display) {
+        rx->pan=rx->pan-(rx->width/2);
+        if(rx->pan<0) rx->pan=0;
+        set_pan(id,rx->pan);
+      } else if(rx_high>=max_display) {
+        rx->pan=rx->pan+(rx->width/2);
+        if(rx->pan>(rx->pixels-rx->width)) rx->pan=rx->pixels-rx->width;
+        set_pan(id,rx->pan);
+      }
+    }
+
     vfo[id].offset=vfo[id].ctun_frequency-vfo[id].frequency;
     if(vfo[id].rit_enabled) {
-       vfo[id].offset+=vfo[id].rit;
+      vfo[id].offset+=vfo[id].rit;
     }
     set_offset(rx,vfo[id].offset);
   } else {
@@ -1318,7 +1336,8 @@ void full_rx_buffer(RECEIVER *rx) {
 
   fexchange0(rx->id, rx->iq_input_buffer, rx->audio_output_buffer, &error);
   if(error!=0) {
-    fprintf(stderr,"full_rx_buffer: id=%d fexchange0: error=%d\n",rx->id,error);
+    //fprintf(stderr,"full_rx_buffer: id=%d fexchange0: error=%d\n",rx->id,error);
+    rx->fexchange_errors++;
   }
 
   if(rx->displaying) {
@@ -1355,3 +1374,35 @@ void add_div_iq_samples(RECEIVER *rx, double i0, double q0, double i1, double q1
     rx->samples=0;
   }
 }
+
+void receiver_change_zoom(RECEIVER *rx,double zoom) {
+  g_mutex_lock(&rx->mutex);
+  if(rx->pixel_samples!=NULL) {
+    g_free(rx->pixel_samples);
+  }
+  rx->pixels=rx->width*(int)zoom;
+  rx->pixel_samples=g_new(float,rx->pixels);
+  rx->hz_per_pixel=(double)rx->sample_rate/(double)rx->pixels;
+  if(zoom==0) {
+    rx->pan=0;
+  } else {
+    if(vfo[rx->id].ctun) {
+      long long min_frequency=vfo[rx->id].frequency-(long long)(rx->sample_rate/2);
+      rx->pan=((vfo[rx->id].ctun_frequency-min_frequency)/rx->hz_per_pixel)-(rx->width/2);
+      if(rx->pan<0) rx->pan=0;
+      if(rx->pan>(rx->pixels-rx->width)) rx->pan=rx->pixels-rx->width;
+    } else {
+      rx->pan=(rx->pixels/2)-(rx->width/2);
+    }
+  }
+  rx->zoom=(int)zoom;
+  init_analyzer(rx);
+  g_mutex_unlock(&rx->mutex);
+}
+
+void receiver_change_pan(RECEIVER *rx,double pan) {
+  g_mutex_lock(&rx->mutex);
+  rx->pan=(int)pan;
+  g_mutex_unlock(&rx->mutex);
+}
+
index 220fec1e757f5d742ba5027133a036754996df84..a0b5be9937e46d0907441c866f19e812fafade6c 100644 (file)
@@ -137,7 +137,14 @@ typedef struct _receiver {
   gint mute_radio;
 
   gdouble *buffer;
-  gint resample_step;
+  void *resampler;
+  gdouble *resample_buffer;
+  gint resample_buffer_size;
+
+  gint fexchange_errors;
+
+  gint zoom;
+  gint pan;
 
   gint x;
   gint y;
@@ -151,6 +158,8 @@ extern void receiver_frequency_changed(RECEIVER *rx);
 extern void receiver_mode_changed(RECEIVER *rx);
 extern void receiver_filter_changed(RECEIVER *rx);
 extern void receiver_vfo_changed(RECEIVER *rx);
+extern void receiver_change_zoom(RECEIVER *rx,double zoom);
+extern void receiver_change_pan(RECEIVER *rx,double pan);
 
 extern void set_mode(RECEIVER* rx,int m);
 extern void set_filter(RECEIVER *rx,int low,int high);
index 37e47a839d0899592b9121cfe8ed06f9a17e65ee..0b18a05999cfc0a0e0c91b36bbc114be4621487f 100644 (file)
@@ -48,6 +48,9 @@ static gfloat filter_left;
 static gfloat filter_right;
 static gfloat cw_frequency;
 
+static gint sequence_error_count=0;
+static gint fexchange_error_count=0;
+
 /* Create a new surface of the appropriate size to store our scribbles */
 static gboolean
 panadapter_configure_event_cb (GtkWidget         *widget,
@@ -146,8 +149,8 @@ void rx_panadapter_update(RECEIVER *rx) {
   }
 
   BAND *band=band_get_band(vfoband);
-  long half=(long)rx->sample_rate/2L;
-  double vfofreq=(double) display_width * 0.5;
+  long long half=(long long)rx->sample_rate/2LL;
+  double vfofreq=((double) rx->pixels * 0.5)-(double)rx->pan;
 
   //
   // There are two options here in CW mode, depending on cw_is_on_vfo_freq.
@@ -171,8 +174,8 @@ void rx_panadapter_update(RECEIVER *rx) {
       vfofreq -= (double) cw_keyer_sidetone_frequency / HzPerPixel;
     }
   }
-  long long min_display=frequency-half;
-  long long max_display=frequency+half;
+  long long min_display=frequency-half+(long long)((double)rx->pan*HzPerPixel);
+  long long max_display=min_display+(long long)((double)rx->width*HzPerPixel);
 
   if(vfoband==band60) {
     for(i=0;i<channel_entries;i++) {
@@ -183,22 +186,15 @@ void rx_panadapter_update(RECEIVER *rx) {
       cairo_set_source_rgb (cr, 0.6, 0.3, 0.3);
       cairo_rectangle(cr, x1, 0.0, x2-x1, (double)display_height);
       cairo_fill(cr);
-/*
-      cairo_set_source_rgba (cr, 0.5, 1.0, 0.0, 1.0);
-      cairo_move_to(cr,(double)x1,0.0);
-      cairo_line_to(cr,(double)x1,(double)display_height);
-      cairo_stroke(cr);
-      cairo_move_to(cr,(double)x2,0.0);
-      cairo_line_to(cr,(double)x2,(double)display_height);
-      cairo_stroke(cr);
-*/
     }
   }
 
   // filter
   cairo_set_source_rgba (cr, 0.25, 0.25, 0.25, 0.75);
-  filter_left =(double)display_width*0.5 +(((double)rx->filter_low+offset)/HzPerPixel);
-  filter_right=(double)display_width*0.5 +(((double)rx->filter_high+offset)/HzPerPixel);
+  //filter_left =(double)display_width*0.5 +(((double)rx->filter_low+offset)/HzPerPixel);
+  //filter_right=(double)display_width*0.5 +(((double)rx->filter_high+offset)/HzPerPixel);
+  filter_left =((double)rx->pixels*0.5)-(double)rx->pan +(((double)rx->filter_low+offset)/HzPerPixel);
+  filter_right=((double)rx->pixels*0.5)-(double)rx->pan +(((double)rx->filter_high+offset)/HzPerPixel);
   cairo_rectangle(cr, filter_left, 0.0, filter_right-filter_left, (double)display_height);
   cairo_fill(cr);
 
@@ -251,32 +247,120 @@ void rx_panadapter_update(RECEIVER *rx) {
   switch(rx->sample_rate) {
     case 48000:
       divisor=5000L;
+      switch(rx->zoom) {
+        case 2:
+        case 3:
+        case 4:
+          divisor=2000L;
+          break;
+        case 5:
+        case 6:
+        case 7:
+        case 8:
+          divisor=1000L;
+          break;
+      }
       break;
     case 96000:
     case 100000:
       divisor=10000L;
+      switch(rx->zoom) {
+        case 2:
+        case 3:
+        case 4:
+          divisor=5000L;
+          break;
+        case 5:
+        case 6:
+          divisor=2000L;
+          break;
+        case 7:
+        case 8:
+          divisor=1000L;
+          break;
+      }
       break;
     case 192000:
       divisor=20000L;
+      switch(rx->zoom) {
+        case 2:
+        case 3:
+          divisor=10000L;
+          break;
+        case 4:
+        case 5:
+        case 6:
+          divisor=5000L;
+          break;
+        case 7:
+        case 8:
+          divisor=2000L;
+          break;
+      }
       break;
     case 384000:
       divisor=50000L;
+      switch(rx->zoom) {
+        case 2:
+        case 3:
+          divisor=25000L;
+          break;
+        case 4:
+        case 5:
+        case 6:
+          divisor=10000L;
+          break;
+        case 7:
+        case 8:
+          divisor=5000L;
+          break;
+      }
       break;
     case 768000:
       divisor=100000L;
+      switch(rx->zoom) {
+        case 2:
+        case 3:
+          divisor=50000L;
+          break;
+        case 4:
+        case 5:
+        case 6:
+          divisor=25000L;
+          break;
+        case 7:
+        case 8:
+          divisor=20000L;
+          break;
+      }
       break;
     case 1048576:
     case 1536000:
     case 2097152:
       divisor=200000L;
+      switch(rx->zoom) {
+        case 2:
+        case 3:
+          divisor=100000L;
+          break;
+        case 4:
+        case 5:
+        case 6:
+          divisor=50000L;
+          break;
+        case 7:
+        case 8:
+          divisor=20000L;
+          break;
+      }
       break;
   }
   for(i=0;i<display_width;i++) {
-    f = frequency - half + (long) (HzPerPixel * i);
+    f = frequency - half + (long) (HzPerPixel * (i+rx->pan));
     if (f > 0) {
       if ((f % divisor) < (long) HzPerPixel) {
         cairo_set_line_width(cr, 1.0);
-        //cairo_move_to(cr,(double)i,0.0);
+        //cairo_move_to(cr,(double)x,0.0);
         cairo_move_to(cr,(double)i,10.0);
         cairo_line_to(cr,(double)i,(double)display_height);
 
@@ -382,12 +466,12 @@ void rx_panadapter_update(RECEIVER *rx) {
   // signal
   double s1,s2;
 
-  samples[0]=-200.0;
-  samples[display_width-1]=-200.0;
+  samples[rx->pan]=-200.0;
+  samples[display_width-1+rx->pan]=-200.0;
   if(have_rx_gain) {
-    s1=(double)samples[0]+rx_gain_calibration-adc_attenuation[rx->adc];
+    s1=(double)samples[rx->pan]+rx_gain_calibration-adc_attenuation[rx->adc];
   } else {
-    s1=(double)samples[0]+(double)adc_attenuation[rx->adc];
+    s1=(double)samples[rx->pan]+(double)adc_attenuation[rx->adc];
   }
   if (filter_board == ALEX && rx->adc == 0) s1 += (double)(10*rx->alex_attenuation);
   if (filter_board == CHARLY25) {
@@ -406,9 +490,9 @@ void rx_panadapter_update(RECEIVER *rx) {
   cairo_move_to(cr, 0.0, s1);
   for(i=1;i<display_width;i++) {
     if(have_rx_gain) {
-      s2=(double)samples[i]+rx_gain_calibration-adc_attenuation[rx->adc];
+      s2=(double)samples[i+rx->pan]+rx_gain_calibration-adc_attenuation[rx->adc];
     } else {
-      s2=(double)samples[i]+(double)adc_attenuation[rx->adc];
+      s2=(double)samples[i+rx->pan]+(double)adc_attenuation[rx->adc];
     }
     if (filter_board == ALEX && rx->adc == 0) s2 += (double)(10*rx->alex_attenuation);
     if (filter_board == CHARLY25) {
@@ -469,6 +553,32 @@ void rx_panadapter_update(RECEIVER *rx) {
   }
 #endif
 
+  if(sequence_errors!=0) {
+    cairo_move_to(cr,100,20);
+    cairo_set_source_rgb(cr,1.0,0.0,0.0);
+    cairo_set_font_size(cr,12);
+    cairo_show_text(cr, "Sequence Error");
+    sequence_error_count++;
+    // show for 1 second
+    if(sequence_error_count==2*rx->fps) {
+      sequence_errors=0;
+      sequence_error_count=0;
+    }
+  }
+
+  if(rx->fexchange_errors!=0) {
+    cairo_move_to(cr,100,30);
+    cairo_set_source_rgb(cr,1.0,0.0,0.0);
+    cairo_set_font_size(cr,12);
+    cairo_show_text(cr, "fexchange Error");
+    fexchange_error_count++;
+    // show for 1 second
+    if(fexchange_error_count==2*rx->fps) {
+      rx->fexchange_errors=0;
+      fexchange_error_count=0;
+    }
+  }
+
   cairo_destroy (cr);
   gtk_widget_queue_draw (rx->panadapter);
 
index b3639e385f3a316a92fbc93c3283ec07cecda051..a8308b2333a6dd647190ea7ef6cf7d1165fa2fa4 100644 (file)
--- a/sliders.c
+++ b/sliders.c
@@ -62,27 +62,11 @@ static int height;
 
 static GtkWidget *sliders;
 
-enum {
-  NO_FUNCTION=0,
-  AF_GAIN,
-  RF_GAIN,
-  MIC_GAIN,
-  LINEIN_GAIN,
-  AGC_GAIN,
-  DRIVE,
-  ATTENUATION,
-  SQUELCH,
-  COMP,
-  FILTER_WIDTH,
-  FILTER_SHIFT,
-  DIVERSITY_GAIN,
-  DIVERSITY_PHASE,
-};
-
-static gint scale_timer;
-static int scale_status=NO_FUNCTION;
-static int scale_rx=0;
-static GtkWidget *scale_dialog;
+gint scale_timer;
+int scale_status=NO_FUNCTION;
+int scale_rx=0;
+GtkWidget *scale_dialog;
+
 static GtkWidget *af_gain_label;
 static GtkWidget *af_gain_scale;
 static GtkWidget *rf_gain_label;
index f303db1ae899b7ff349a78eb514ca30848dc7471..7c981c7c4ffba31476f71358429b802267735fac 100644 (file)
--- a/sliders.h
+++ b/sliders.h
 #include "receiver.h"
 #include "transmitter.h"
 
+enum {
+  NO_FUNCTION=0,
+  AF_GAIN,
+  RF_GAIN,
+  MIC_GAIN,
+  LINEIN_GAIN,
+  AGC_GAIN,
+  DRIVE,
+  ATTENUATION,
+  SQUELCH,
+  COMP,
+  FILTER_WIDTH,
+  FILTER_SHIFT,
+  DIVERSITY_GAIN,
+  DIVERSITY_PHASE,
+  ZOOM,
+  PAN
+};
+
+extern gint scale_timer;
+extern gint scale_status;
+extern gint scale_rx;
+extern GtkWidget *scale_dialog;
+int scale_timeout_cb(gpointer data);
+
 extern void att_type_changed(void);
 extern void update_att_preamp(void);
 
index db472d94b1056a3873cd26daf949a7bd622d4674..630d34d9887284c14e04979e6ea4ccd5d9a8c21d 100644 (file)
@@ -117,6 +117,17 @@ static void get_info(char *driver) {
   }
   fprintf(stderr,"\n");
   free(rx_rates);
+
+  if(strcmp(driver,"lime")==0) {
+    sample_rate=768000;
+  } else if(strcmp(driver,"rtlsdr")==0) {
+    sample_rate=1024000;
+  } else if(strcmp(driver,"plutosdr")==0) {
+    sample_rate=2048000;
+  } else {
+    sample_rate=1024000;
+  }
+
   fprintf(stderr,"sample_rate selected %d\n",sample_rate);
 
   SoapySDRRange *tx_rates=SoapySDRDevice_getSampleRateRange(sdr, SOAPY_SDR_TX, 1, &tx_rates_length);
index 6a988e6d65e27a7297b7c4d38df277b0c8d7ac40..95fca283f3288f508734b044171b81351b8bf19e 100644 (file)
@@ -57,7 +57,6 @@ static double bandwidth=2500000.0;
 static SoapySDRDevice *soapy_device;
 static SoapySDRStream *rx_stream;
 static SoapySDRStream *tx_stream;
-static int soapy_rx_sample_rate;
 static int max_samples;
 
 static int samples=0;
@@ -86,28 +85,50 @@ SoapySDRDevice *get_soapy_device() {
 
 void soapy_protocol_set_mic_sample_rate(int rate) {
   mic_sample_divisor=rate/48000;
+g_print("soapy_protocol_set_mic_sample_rate: rate=%d mic_sample_divisor=%d\n",rate,mic_sample_divisor);
 }
 
-void soapy_protocol_change_sample_rate(RECEIVER *rx,int rate) {
+void soapy_protocol_change_sample_rate(RECEIVER *rx) {
+g_print("soapy_protocol_change_sample_rate: %d\n",rx->sample_rate);
+  if(rx->sample_rate==radio_sample_rate) {
+    if(rx->resample_buffer!=NULL) {
+      g_free(rx->resample_buffer);
+      rx->resample_buffer=NULL;
+      rx->resample_buffer_size=0;
+    }
+    if(rx->resampler!=NULL) {
+      destroy_resample(rx->resampler);
+      rx->resampler=NULL;
+    }
+  } else {
+    if(rx->resample_buffer!=NULL) {
+      g_free(rx->resample_buffer);
+      rx->resample_buffer=NULL;
+    }
+    if(rx->resampler!=NULL) {
+      destroy_resample(rx->resampler);
+      rx->resampler=NULL;
+    }
+    rx->resample_buffer_size=2*max_samples/(radio_sample_rate/rx->sample_rate);
+    rx->resample_buffer=g_new(double,rx->resample_buffer_size);
+    rx->resampler=create_resample (1,max_samples,rx->buffer,rx->resample_buffer,radio_sample_rate,rx->sample_rate,0.0,0,1.0);
+
+g_print("soapy_protocol_change_sample_rate: buffer=%p buffer_size=%d resampler=%p\n",rx->resample_buffer,rx->resample_buffer_size,rx->resampler);
+  }
+
 }
 
 void soapy_protocol_create_receiver(RECEIVER *rx) {
   int rc;
 
-  soapy_rx_sample_rate=rx->sample_rate;
-  if(rx->sample_rate!=radio_sample_rate) {
-    soapy_rx_sample_rate=radio_sample_rate;
-  }
-  mic_sample_divisor=soapy_rx_sample_rate/48000;
+  mic_sample_divisor=rx->sample_rate/48000;
 
-fprintf(stderr,"soapy_protocol_create_receiver: setting samplerate=%f adc=%d mic_sample_divisor=%d\n",(double)soapy_rx_sample_rate,rx->adc,mic_sample_divisor);
-  rc=SoapySDRDevice_setSampleRate(soapy_device,SOAPY_SDR_RX,rx->adc,(double)soapy_rx_sample_rate);
+fprintf(stderr,"soapy_protocol_create_receiver: setting samplerate=%f adc=%d mic_sample_divisor=%d\n",(double)radio_sample_rate,rx->adc,mic_sample_divisor);
+  rc=SoapySDRDevice_setSampleRate(soapy_device,SOAPY_SDR_RX,rx->adc,(double)radio_sample_rate);
   if(rc!=0) {
-    fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setSampleRate(%f) failed: %s\n",(double)soapy_rx_sample_rate,SoapySDR_errToStr(rc));
+    fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setSampleRate(%f) failed: %s\n",(double)radio_sample_rate,SoapySDR_errToStr(rc));
   }
 
-
-
   size_t channel=rx->adc;
 #if defined(SOAPY_SDR_API_VERSION) && (SOAPY_SDR_API_VERSION < 0x00080000)
 fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setupStream(version<0x00080000): channel=%ld\n",channel);
@@ -130,8 +151,26 @@ fprintf(stderr,"soapy_protocol_create_receiver: SoapySDRDevice_setupStream(versi
   if(max_samples>(2*rx->fft_size)) {
     max_samples=2*rx->fft_size;
   }
+  if(max_samples>=4096) {
+    max_samples=4096;
+  } else if(max_samples>=2048) {
+    max_samples=2048;
+  } else {
+    max_samples=1024;
+  }
   rx->buffer=g_new(double,max_samples*2);
 
+  if(rx->sample_rate==radio_sample_rate) {
+    rx->resample_buffer=NULL;
+    rx->resampler=NULL;
+    rx->resample_buffer_size=0;
+  } else {
+    rx->resample_buffer_size=2*max_samples/(radio_sample_rate/rx->sample_rate);
+    rx->resample_buffer=g_new(double,rx->resample_buffer_size);
+    rx->resampler=create_resample (1,max_samples,rx->buffer,rx->resample_buffer,radio_sample_rate,rx->sample_rate,0.0,0,1.0);
+  }
+
+
 fprintf(stderr,"soapy_protocol_create_receiver: max_samples=%d buffer=%p\n",max_samples,rx->buffer);
 
 }
@@ -278,10 +317,13 @@ fprintf(stderr,"soapy_protocol: receive_thread\n");
       rx->buffer[i*2]=(double)buffer[i*2];
       rx->buffer[(i*2)+1]=(double)buffer[(i*2)+1];
     }
-    if(rx->sample_rate!=radio_sample_rate) {
-      for(int i=0;i<elements;i+=rx->resample_step) {
-        isample=rx->buffer[i*2];
-        qsample=rx->buffer[(i*2)+1];
+
+
+    if(rx->resampler!=NULL) {
+      int samples=xresample(rx->resampler);
+      for(i=0;i<samples;i++) {
+        isample=rx->resample_buffer[i*2];
+        qsample=rx->resample_buffer[(i*2)+1];
         if(iqswap) {
           add_iq_samples(rx,qsample,isample);
         } else {
@@ -290,7 +332,11 @@ fprintf(stderr,"soapy_protocol: receive_thread\n");
         if(can_transmit) {
           mic_samples++;
           if(mic_samples>=mic_sample_divisor) { // reduce to 48000
-            fsample = transmitter->local_microphone ? audio_get_next_mic_sample() : (float)0.0;
+            if(transmitter!=NULL) {
+              fsample = transmitter->local_microphone ? audio_get_next_mic_sample() : 0.0F;
+            } else {
+              fsample=0.0F;
+            }
             add_mic_sample(transmitter,fsample);
             mic_samples=0;
           }
@@ -308,7 +354,11 @@ fprintf(stderr,"soapy_protocol: receive_thread\n");
         if(can_transmit) {
           mic_samples++;
           if(mic_samples>=mic_sample_divisor) { // reduce to 48000
-            fsample = transmitter->local_microphone ? audio_get_next_mic_sample() : (float)0.0;
+            if(transmitter!=NULL) {
+              fsample = transmitter->local_microphone ? audio_get_next_mic_sample() : 0.0F;
+            } else {
+              fsample=0.0F;
+            }
             add_mic_sample(transmitter,fsample);
             mic_samples=0;
           }
index 50608913fd944b77504df3eec3b10ae6558a3251..541dbf92974b1634a9d8af5e71c95c77cabc156b 100644 (file)
@@ -35,7 +35,7 @@ void soapy_protocol_set_lna_gain(RECEIVER *rx,int gain);
 void soapy_protocol_set_gain(RECEIVER *rx,double gain);
 void soapy_protocol_set_gain_element(RECEIVER *rx,char *name,int gain);
 int soapy_protocol_get_gain_element(RECEIVER *rx,char *name);
-void soapy_protocol_change_sample_rate(RECEIVER *rx,int rate);
+void soapy_protocol_change_sample_rate(RECEIVER *rx);
 gboolean soapy_protocol_get_automatic_gain(RECEIVER *rx);
 void soapy_protocol_set_automatic_gain(RECEIVER *rx,gboolean mode);
 void soapy_protocol_create_transmitter(TRANSMITTER *tx);
diff --git a/vfo.c b/vfo.c
index 8ba8a38c61b35303e05bf117011c4f072efbcda6..c8811e7d161d2eb70584b5be5e807229311d1825 100644 (file)
--- a/vfo.c
+++ b/vfo.c
@@ -533,6 +533,20 @@ void vfo_step(int steps) {
   if(!locked) {
 
     if(vfo[id].ctun) {
+      // don't let ctun go beyond end of passband
+      long long frequency=vfo[id].frequency;
+      long long rx_low=((vfo[id].ctun_frequency/step + steps)*step)+active_receiver->filter_low;
+      long long rx_high=((vfo[id].ctun_frequency/step + steps)*step)+active_receiver->filter_high;
+      long long half=(long long)active_receiver->sample_rate/2LL;
+      long long min_freq=frequency-half;
+      long long max_freq=frequency+half;
+
+      if(rx_low<=min_freq) {
+        return;
+      } else if(rx_high>=max_freq) {
+        return;
+      }
+
       delta=vfo[id].ctun_frequency;
       vfo[id].ctun_frequency=(vfo[id].ctun_frequency/step + steps)*step;
       delta=vfo[id].ctun_frequency - delta;
@@ -639,6 +653,20 @@ void vfo_move(long long hz,int round) {
 
   if(!locked) {
     if(vfo[id].ctun) {
+      // don't let ctun go beyond end of passband
+      long long frequency=vfo[id].frequency;
+      long long rx_low=vfo[id].ctun_frequency+hz+active_receiver->filter_low;
+      long long rx_high=vfo[id].ctun_frequency+hz+active_receiver->filter_high;
+      long long half=(long long)active_receiver->sample_rate/2LL;
+      long long min_freq=frequency-half;
+      long long max_freq=frequency+half;
+
+      if(rx_low<=min_freq) {
+        return;
+      } else if(rx_high>=max_freq) {
+        return;
+      }
+
       delta=vfo[id].ctun_frequency;
       vfo[id].ctun_frequency=vfo[id].ctun_frequency+hz;
       if(round && (vfo[id].mode!=modeCWL && vfo[id].mode!=modeCWU)) {
@@ -689,7 +717,7 @@ void vfo_move(long long hz,int round) {
 }
 
 void vfo_move_to(long long hz) {
-  // hz is the offset from the min frequency
+  // hz is the offset from the min displayed frequency
   int id=active_receiver->id;
   long long offset=hz;
   long long half=(long long)(active_receiver->sample_rate/2);
@@ -701,7 +729,7 @@ void vfo_move_to(long long hz) {
   if(vfo[id].mode!=modeCWL && vfo[id].mode!=modeCWU) {
     offset=(hz/step)*step;
   }
-  f=(vfo[id].frequency-half)+offset;
+  f=(vfo[id].frequency-half)+offset+((double)active_receiver->pan*active_receiver->hz_per_pixel);
 
   if(!locked) {
     if(vfo[id].ctun) {
@@ -894,8 +922,7 @@ void vfo_update() {
 
 #ifdef PURESIGNAL
         if(can_transmit) {
-          //cairo_move_to(cr, 180, 15);
-          cairo_move_to(cr, 55, 50);
+          cairo_move_to(cr, 130, 50);
           if(transmitter->puresignal) {
             cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
           } else {
@@ -905,7 +932,16 @@ void vfo_update() {
           cairo_show_text(cr, "PS");
         }
 #endif
-
+        
+        cairo_move_to(cr, 55, 50);
+        if(active_receiver->zoom>1) {
+          cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
+        } else {
+          cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
+        }
+        cairo_set_font_size(cr, 12);
+        sprintf(temp_text,"Zoom x%d",active_receiver->zoom);
+        cairo_show_text(cr, temp_text);
 
         if(vfo[id].rit_enabled==0) {
             cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
@@ -913,7 +949,6 @@ void vfo_update() {
             cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
         }
         sprintf(temp_text,"RIT: %lldHz",vfo[id].rit);
-        //cairo_move_to(cr, 210, 15);
         cairo_move_to(cr, 170, 15);
         cairo_set_font_size(cr, 12);
         cairo_show_text(cr, temp_text);
@@ -975,7 +1010,6 @@ void vfo_update() {
         }
         cairo_show_text(cr, "SNB");
 
-        //cairo_move_to(cr, 300, 50);  
         cairo_move_to(cr, 270, 50);  
         switch(active_receiver->agc) {
           case AGC_OFF:
@@ -1005,7 +1039,6 @@ void vfo_update() {
        // we should display the compressor (level)
        //
         if(can_transmit) {
-          //cairo_move_to(cr, 400, 50);  
           cairo_move_to(cr, 330, 50);  
          if (transmitter->compressor) {
              sprintf(temp_text,"CMPR %d dB",(int) transmitter->compressor_level);
@@ -1030,17 +1063,10 @@ void vfo_update() {
           s++;
         }
         sprintf(temp_text,"Step %s",step_labels[s]);
-        //cairo_move_to(cr, 300, 15);
         cairo_move_to(cr, 400, 15);
         cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
         cairo_show_text(cr, temp_text);
 
-/*
-        cairo_move_to(cr, (my_width/4)*3, 50);
-        cairo_show_text(cr, getFrequencyInfo(af));
-*/
-         
-        //cairo_move_to(cr, 400, 15);  
         cairo_move_to(cr, 430, 50);  
         if(vfo[id].ctun) {
           cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
@@ -1049,7 +1075,6 @@ void vfo_update() {
         }
         cairo_show_text(cr, "CTUN");
 
-        //cairo_move_to(cr, 450, 15);  
         cairo_move_to(cr, 470, 50);  
         if(cat_control>0) {
           cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
@@ -1076,7 +1101,6 @@ void vfo_update() {
         }
         cairo_show_text(cr, "Locked");
 
-        //cairo_move_to(cr, 55, 50);
         cairo_move_to(cr, 260, 18);
         if(split) {
           cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
@@ -1085,7 +1109,6 @@ void vfo_update() {
         }
         cairo_show_text(cr, "Split");
 
-        //cairo_move_to(cr, 95, 50);
         cairo_move_to(cr, 260, 28);
         if(sat_mode!=SAT_NONE) {
           cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
@@ -1105,7 +1128,6 @@ void vfo_update() {
             cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
         }
         sprintf(temp_text,"DUP");
-        //cairo_move_to(cr, 130, 50);
         cairo_move_to(cr, 260, 38);
         cairo_set_font_size(cr, 12);
         cairo_show_text(cr, temp_text);
index 376d11416f7d286a9aed4d90f2881a3c38732f93..8166d0883ce898bf472f9ccc4e13ad1f7df59721 100644 (file)
@@ -173,9 +173,9 @@ void waterfall_update(RECEIVER *rx) {
     samples=rx->pixel_samples;
     for(i=0;i<width;i++) {
             if(have_rx_gain) {
-              sample=samples[i]+(float)(rx_gain_calibration-adc_attenuation[rx->adc]);
+              sample=samples[i+rx->pan]+(float)(rx_gain_calibration-adc_attenuation[rx->adc]);
             } else {
-              sample=samples[i]+(float)adc_attenuation[rx->adc];
+              sample=samples[i+rx->pan]+(float)adc_attenuation[rx->adc];
             }
             average+=(int)sample;
             if(sample<(float)rx->waterfall_low) {
diff --git a/zoompan.c b/zoompan.c
new file mode 100644 (file)
index 0000000..2aee381
--- /dev/null
+++ b/zoompan.c
@@ -0,0 +1,211 @@
+/* Copyright (C)
+* 2015 - 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.
+*
+*/
+
+//
+// DL1YCF:
+// uncomment the #define line following, then you will get
+// a "TX compression" slider with an enabling checkbox
+// in the bottom right of the sliders area, instead of the
+// sequelch slider and checkbox.
+// This option can also be passed to the compiler with "-D"
+// and thus be activated through the Makefile.
+//
+//#define COMPRESSION_SLIDER_INSTEAD_OF_SQUELCH 1
+//
+
+#include <gtk/gtk.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "main.h"
+#include "receiver.h"
+#include "radio.h"
+#include "vfo.h"
+#include "sliders.h"
+#include "zoompan.h"
+
+static int width;
+static int height;
+
+static GtkWidget *zoompan;
+static GtkWidget *zoom_label;
+static GtkWidget *zoom_scale;
+static GtkWidget *pan_label;
+static GtkWidget *pan_scale;
+
+static GdkRGBA white;
+static GdkRGBA gray;
+
+int zoompan_active_receiver_changed(void *data) {
+  if(display_zoompan) {
+    gtk_range_set_value(GTK_RANGE(zoom_scale),active_receiver->zoom);
+    gtk_range_set_range(GTK_RANGE(pan_scale),0.0,(double)(active_receiver->pixels-active_receiver->width));
+    gtk_range_set_value (GTK_RANGE(pan_scale),active_receiver->pan);
+    if(active_receiver->zoom == 1) {
+      gtk_widget_set_sensitive(pan_scale, FALSE);
+    }
+  }
+  return FALSE;
+}
+
+static void zoom_value_changed_cb(GtkWidget *widget, gpointer data) {
+  receiver_change_zoom(active_receiver,gtk_range_get_value(GTK_RANGE(zoom_scale)));
+  gtk_range_set_range(GTK_RANGE(pan_scale),0.0,(double)(active_receiver->pixels-active_receiver->width));
+  gtk_range_set_value (GTK_RANGE(pan_scale),active_receiver->pan);
+  if(active_receiver->zoom == 1) {
+    gtk_widget_set_sensitive(pan_scale, FALSE);
+  } else {
+    gtk_widget_set_sensitive(pan_scale, TRUE);
+  }
+  vfo_update();
+}
+
+void set_zoom(int rx,double value) {
+  receiver[rx]->zoom=value;
+  if(display_zoompan) {
+    gtk_range_set_value (GTK_RANGE(zoom_scale),receiver[rx]->zoom);
+  } else {
+    if(scale_status!=ZOOM || scale_rx!=rx) {
+      if(scale_status!=NO_FUNCTION) {
+        g_source_remove(scale_timer);
+        gtk_widget_destroy(scale_dialog);
+        scale_status=NO_FUNCTION;
+      }
+    }
+    if(scale_status==NO_FUNCTION) {
+      scale_status=ZOOM;
+      scale_rx=rx;
+      char title[64];
+      sprintf(title,"Zoom RX %d",rx);
+      scale_dialog=gtk_dialog_new_with_buttons(title,GTK_WINDOW(top_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+      GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(scale_dialog));
+      zoom_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,1.0, MAX_ZOOM, 1.00);
+      gtk_widget_set_size_request (zoom_scale, 400, 30);
+      gtk_range_set_value (GTK_RANGE(zoom_scale),receiver[rx]->zoom);
+      gtk_widget_show(zoom_scale);
+      gtk_container_add(GTK_CONTAINER(content),zoom_scale);
+      scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL);
+      gtk_dialog_run(GTK_DIALOG(scale_dialog));
+    } else {
+      g_source_remove(scale_timer);
+      gtk_range_set_value (GTK_RANGE(zoom_scale),receiver[rx]->zoom);
+      scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL);
+    }
+  }
+  vfo_update();
+}
+
+void update_zoom(double zoom) {
+  int z=active_receiver->zoom+(int)zoom;
+  if(z>MAX_ZOOM) z=MAX_ZOOM;
+  if(z<1) z=1;
+  set_zoom(active_receiver->id,z);
+}
+
+static void pan_value_changed_cb(GtkWidget *widget, gpointer data) {
+  receiver_change_pan(active_receiver,gtk_range_get_value(GTK_RANGE(pan_scale)));
+}
+
+void set_pan(int rx,double value) {
+  receiver[rx]->pan=(int)value;
+  if(display_zoompan) {
+    gtk_range_set_value (GTK_RANGE(pan_scale),receiver[rx]->pan);
+  } else {
+    if(scale_status!=PAN || scale_rx!=rx) {
+      if(scale_status!=NO_FUNCTION) {
+        g_source_remove(scale_timer);
+        gtk_widget_destroy(scale_dialog);
+        scale_status=NO_FUNCTION;
+      }
+    }
+    if(scale_status==NO_FUNCTION) {
+      scale_status=PAN;
+      scale_rx=rx;
+      char title[64];
+      sprintf(title,"Pan RX %d",rx);
+      scale_dialog=gtk_dialog_new_with_buttons(title,GTK_WINDOW(top_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+      GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(scale_dialog));
+      pan_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, active_receiver->zoom==1?active_receiver->pixels:active_receiver->pixels-active_receiver->width, 1.00);
+      gtk_widget_set_size_request (pan_scale, 400, 30);
+      gtk_range_set_value (GTK_RANGE(pan_scale),receiver[rx]->pan);
+      gtk_widget_show(pan_scale);
+      gtk_container_add(GTK_CONTAINER(content),pan_scale);
+      scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL);
+      gtk_dialog_run(GTK_DIALOG(scale_dialog));
+    } else {
+      g_source_remove(scale_timer);
+      gtk_range_set_value (GTK_RANGE(pan_scale),receiver[rx]->pan);
+      scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL);
+    }
+  }
+}
+
+void update_pan(double pan) {
+  int p=active_receiver->pan+(int)pan;
+  if(p<0) p=0;
+  if(p>(active_receiver->pixels-active_receiver->width)) p=active_receiver->pixels-active_receiver->width;
+  set_pan(active_receiver->id,p);
+}
+
+GtkWidget *zoompan_init(int my_width, int my_height) {
+  width=my_width;
+  height=my_height;
+
+fprintf(stderr,"zoompan_init: width=%d height=%d\n", width,height);
+
+  zoompan=gtk_grid_new();
+  gtk_widget_set_size_request (zoompan, width, height);
+  gtk_grid_set_row_homogeneous(GTK_GRID(zoompan), FALSE);
+  gtk_grid_set_column_homogeneous(GTK_GRID(zoompan),TRUE);
+
+  zoom_label=gtk_label_new("Zoom");
+  gtk_widget_override_font(zoom_label, pango_font_description_from_string("Sans 10"));
+  gtk_widget_show(zoom_label);
+  gtk_grid_attach(GTK_GRID(zoompan),zoom_label,0,0,1,1);
+
+  zoom_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,1.0,MAX_ZOOM,1.00);
+  gtk_widget_override_font(zoom_scale, pango_font_description_from_string("Sans 10"));
+  gtk_range_set_increments (GTK_RANGE(zoom_scale),1.0,1.0);
+  gtk_range_set_value (GTK_RANGE(zoom_scale),active_receiver->zoom);
+  gtk_widget_show(zoom_scale);
+  gtk_grid_attach(GTK_GRID(zoompan),zoom_scale,1,0,2,1);
+  g_signal_connect(G_OBJECT(zoom_scale),"value_changed",G_CALLBACK(zoom_value_changed_cb),NULL);
+
+  pan_label=gtk_label_new("Pan:");
+  gtk_widget_override_font(pan_label, pango_font_description_from_string("Sans 10"));
+  gtk_widget_show(pan_label);
+  gtk_grid_attach(GTK_GRID(zoompan),pan_label,3,0,1,1);
+
+  pan_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0,active_receiver->zoom==1?active_receiver->pixels:active_receiver->pixels-active_receiver->width,1.0);
+  gtk_widget_override_font(pan_scale, pango_font_description_from_string("Sans 10"));
+  gtk_scale_set_draw_value (GTK_SCALE(pan_scale), FALSE);
+  gtk_range_set_increments (GTK_RANGE(pan_scale),10.0,10.0);
+  gtk_range_set_value (GTK_RANGE(pan_scale),active_receiver->pan);
+  gtk_widget_show(pan_scale);
+  gtk_grid_attach(GTK_GRID(zoompan),pan_scale,4,0,5,1);
+  g_signal_connect(G_OBJECT(pan_scale),"value_changed",G_CALLBACK(pan_value_changed_cb),NULL);
+
+  if(active_receiver->zoom == 1) {
+    gtk_widget_set_sensitive(pan_scale, FALSE);
+  }
+
+  return zoompan;
+}
diff --git a/zoompan.h b/zoompan.h
new file mode 100644 (file)
index 0000000..833d6f7
--- /dev/null
+++ b/zoompan.h
@@ -0,0 +1,31 @@
+/* Copyright (C)
+* 2015 - John Melton, G0ORX/N6LYT
+*
+* 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 _ZOOMPAN_H
+#define _ZOOMPAN_H
+
+#define MAX_ZOOM 8
+
+extern GtkWidget *zoompan_init(int my_width, int my_height);
+extern int zoompan_active_receiver_changed(void *data);
+
+extern void update_pan(double pan);
+extern void update_zoom(double zoom);
+
+extern void set_pan(int rx,double value);
+extern void set_zoom(int rx,double value);
+#endif