Drawing an S-meter

s-meter2

Now some graphics. This is supposed to be an S-meter. The length of the yellow bar shows the signal strength. The refresh rate is 10 frames per second, which means I am inquiring IC-7410 for the S-meter value every 100 mS.

I need to do some calibrations. The only available information from the instruction manual is that 0000=S0, 0120=S9, and 0240=S9+60dB.

CPU2

The CPU load drops significantly when the program is terminated.

With grig there is almost no change in the CPU load, so there is a room for improvement.

Read Operating Frequency from IC-7410

freqnow

Now gathering information from IC-7410.

gboolean freq_read( gpointer data ) {
  int outputcount = 6;
  static char command1[6] = {0xfe, 0xfe, 0x80, 0xe0, 0x03, 0xfd}; /* Read Operating Freq */
  unsigned char buf[255];
  int res;
  char string[128];
  double freq;

  write(fd, &command1, outputcount);
  while( (res = read(fd,buf,255)) !=11 ) {
   ;
  }
/* freq data in buf[8]-[5] */
   sprintf(string, "%02x%02x%02x%02x", buf[8],buf[7],buf[6],buf[5]);
   freq = (double) atoi(string) / 1000.0;
   g_print("freq is now %10.3f kHz \n", freq);
  return TRUE; /* MUST return TRUE */
}

I am not checking the response from IC-7410 strictly, just observing if its length is as expected or not.

/* main() */
    g_timeout_add( (guint) 250, (GSourceFunc) freq_read, (gpointer) "test" );

freqdisplay

Getting S-meter level should be likewise. The rest of the problem is how you can generate nice looking displays out of just numbers. (The above figure from grig.)

Radio Buttons (2)

radiofrq

Radio buttons for preset frequencies.

void set_freq (double frq) { /* freq in kHz */
  int outputcount = 11;
  static unsigned char command[11] /* set frequency command */
   = {0xfe, 0xfe, 0x80, 0xe0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd};
  int ifrq, idigit[8];

  ifrq = frq * 1000; /* freq in Hz */
  for(int i=0; i<8; i++) {
   idigit[i] = ifrq % 10;
   ifrq /= 10;
  }
  command[5] = 16*idigit[1] + idigit[0];
  command[6] = 16*idigit[3] + idigit[2];
  command[7] = 16*idigit[5] + idigit[4];
  command[8] = 16*idigit[7] + idigit[6];
  write(fd, &command1, outputcount);
}

I always wonder if I should use a for loop for the lines like command[5] through [8]. I suppose repeating the lines is more explicit and more readable.

/* main */
    int nfrq = 8;
    char *strfrq[] = {" 3501.000kHz", " 7026.000kHz", "10118.000kHz", "14058.000kHz",
                      "18085.000kHz", "21058.000kHz", "24908.000kHz", "28058.000kHz"};

    for(int i=0;i<nfrq;i++) {
     if(i == 0) {
      button = gtk_radio_button_new_with_label (NULL, strfrq[i]);
     } else {
      button = gtk_radio_button_new_with_label(gtk_radio_button_group (GTK_RADIO_BUTTON (button)), strfrq[i]);
     }
     gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (callback3), (gpointer) strfrq[i]);
     gtk_box_pack_start (GTK_BOX (box6), button, TRUE, TRUE, 0);
     gtk_widget_show (button);
    }

Radio Buttons

radiobutton

It works, but initially a signal is issued that tells “10 wpm” is toggled. This is strange because the default depressed button is set to “30 wpm”.

void callback2( GtkWidget *widget, gpointer data ) {
 int wpm;
 wpm = atoi( g_strndup((char *) data, 2) );
 set_cw_speed(wpm);
 g_print ("%s was toggled \n", (char *) data);
}

The callback routine is almost the same as before. No need to check the suffix of “wpm” here.

/* main() */
    char *strwpm[] = {"10 wpm", "15 wpm", "20 wpm", "25 wpm", "30 wpm", "35 wpm", "40 wpm"};

    for(int i=0;i<7;i++) {
     if(i == 0) {
      button = gtk_radio_button_new_with_label(NULL, strwpm[i]);
     } else {
      button = gtk_radio_button_new_with_label(gtk_radio_button_group (GTK_RADIO_BUTTON (button)), strwpm[i]);
     }
     if(i == 4) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
     gtk_signal_connect (GTK_OBJECT (button), "toggled", GTK_SIGNAL_FUNC (callback2), (gpointer) strwpm[i]);
     gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
     gtk_widget_show (button);
    }

Initially, the fourth button (30 wpm) is depressed. (Yes, we count from the number zero.)

% ./a.out
10 wpm was toggled <-- before I touch no buttons.

This is not what I expected, but it should not be a big matter, because I will need some initialization for IC-7410 anyway.

Key Speed Control

cwspeed

Perhaps a set of buttons are easier to use than a slider for changing the key speed.

    struct mybutton mybuttons[100] = {
     {"CW",         0, 1, 0, 1},
     {"CW-REV",     0, 1, 1, 2},
     {"10 wpm",     1, 2, 0, 1},
     {"15 wpm",     1, 2, 1, 2},
     {"20 wpm",     1, 2, 2, 3},
     {"25 wpm",     1, 2, 3, 4},
     {"30 wpm",     1, 2, 4, 5},
     {"35 wpm",     1, 2, 5, 6},
     {"40 wpm",     1, 2, 6, 7}
    }
    for(int i=0;i<nbutton;i++) {
      button = gtk_button_new_with_label (mybuttons[i].name);
      gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (callback),
                          (gpointer) mybuttons[i].name);
      gtk_table_attach_defaults(GTK_TABLE(table), button,
                                mybuttons[i].left_attach, mybuttons[i].right_attach,
                                mybuttons[i].top_attach,  mybuttons[i].bottom_attach);
      gtk_widget_show (button);
    }

First you define lots of buttons, their labels and locations. IF one of the buttons is “clicked”, a callback routine is called.

void callback( GtkWidget *widget, gpointer data ) {
  gint   wpm;
  if(g_str_has_suffix((char *)data, "wpm")) {
   wpm = atoi( g_strndup((char *) data, 2) );
   set_cw_speed(wpm);
  }
}

If the label of a button ends with “wpm”, we have to change the key speed according to the first two digits.

void set_cw_speed(int wpm) {
  int outputcount = 9;
  static unsigned char command1[9] = {0xfe, 0xfe, 0x80, 0xe0, 0x14, 0x0c, 0x00, 0x32, 0xfd};
  int iii, i100, i10, i1;
  if(wpm <  6) wpm =  6;
  if(wpm > 48) wpm = 48;
  iii = 255 * (wpm - 6) / (48 - 6);
  i100 = iii / 100;
  i10  = (iii - 100*i100) / 10;
  i1   = iii % 10;
  command1[6] = i100;
  command1[7] = 16*i10 + i1;
  write(fd, &command1, outputcount);
}

Conversion from “wpm” to a command parameter is necessary. See IC-7410 Keyspeed for the details.

AF Volume and Key Speed

AFvolume

This is a callback function for a scale change.

void scale_value_changed_for_AF_Volume( GtkWidget *scale, gpointer   data ) {
  int outputcount = 9;
  static unsigned char command[9] = {0xfe, 0xfe, 0x80, 0xe0, 0x14, 0x01, 0x00, 0x32, 0xfd};
  int value, i100, i10, i1;
  value = gtk_range_get_value(GTK_RANGE (scale));
  i100 = value / 100;
  i10  = (value - 100*i100) / 10;
  i1   = value % 10;
  command[6] = i100;
  command[7] = 16*i10 + i1; /* BCD coded */
  write(fd, &command, outputcount);
}

And here is a main program.

    adj = gtk_adjustment_new(50.0, 0.0, 256.0, 1.0, 1.0, 1.0);
    scale = gtk_hscale_new(GTK_ADJUSTMENT (adj));
    gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_TOP);
    gtk_signal_connect (GTK_OBJECT (scale), "value_changed", GTK_SIGNAL_FUNC (scale_value_changed_for_AF_Volume), NULL);
    label = gtk_label_new_with_mnemonic ("AF Volume");
    box = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX (box), scale, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX (box), label,  TRUE, TRUE, 0);

If you change the sliders, IC-7410 will respond perfectly, and it is much less frustrating to write a code yourself than deciphering the exiting ones.

GTK+ and IC-7410 (4)

Now the callback function is modified to include the lines for sending a command that relates to the button clicked.

void callback( GtkWidget *widget, gpointer   data ) {
  static char output[128];
  int outputcount;
  static char command_cw    [7] = {0xfe, 0xfe, 0x80, 0xe0, 0x06, 0x03, 0xfd}; /* set mode to CW */
  static char command_cw-rev[7] = {0xfe, 0xfe, 0x80, 0xe0, 0x06, 0x07, 0xfd}; /* set mode to CW-REV */

  if(g_strcmp0((char *) data, "CW") == 0) {
   outputcount = 7; /* command length */
   write(fd, &command_cw, outputcount);
  }
  if(g_strcmp0((char *) data, "CW-REV") == 0) {
   outputcount = 7; /* command length */
  write(fd, &command_cw-rev, outputcount);
  }
}

The above code is not complete, but you can see what I am trying to do.

#define MYRIG "/dev/ttyUSB0"
#define  BAUDRATE B19200

void serial_init(void) {
  struct termios tio;
  memset(&tio, 0, sizeof(tio));
  tio.c_cflag     = CS8 | CLOCAL | CREAD;
  tio.c_cc[VTIME] = 0;
  tio.c_cc[VEOL ] = 0xfd; /* IC-7410 postamble */
  tio.c_lflag     = ICANON;
  tio.c_iflag     = IGNPAR | ICRNL;
  cfsetispeed(&tio, BAUDRATE);
  cfsetospeed(&tio, BAUDRATE);
  tcsetattr  (fd, TCSANOW, &tio);
}
/* main() */
    fd = open(MYRIG, O_RDWR | O_NOCTTY);
    serial_init();

You also need to add the above lines before talking to IC-7410. Lines for error check are omitted for clarity. See CW Keyboard (2) for an complete example.

slider

Some scale widgets are added.

GTK+ and IC-7410 (3)

paneldesign

Maybe these are all the buttons I need for daily operations.

#include <gtk/gtk.h>

struct mybutton {
 char name[100];
 int  left_attach;
 int  right_attach;
 int  top_attach;
 int  bottom_attach;
};

void callback( GtkWidget *widget, gpointer   data ) {
    g_print ("%s was pressed\n", (char *) data);
}

gint delete_event( GtkWidget *widget, GdkEvent *event, gpointer data ) {
    gtk_main_quit ();
    return(FALSE);
}

int main( int   argc, char *argv[] ) {

    int nx=20, ny=10;
    int nbutton = 25;
    struct mybutton mybuttons[100] = {
     {"CW",         0, 1, 0, 1},
     {"CW-REV",     0, 1, 1, 2},
     {"20 wpm",     1, 2, 0, 1},
     {"25 wpm",     1, 2, 1, 2},
     {"30 wpm",     1, 2, 2, 3},
     {"35 wpm",     1, 2, 3, 4},
     {"40 wpm",     1, 2, 4, 5},
     {"TX OFF",     3, 4, 0, 1},
     {"BK-IN",      3, 4, 1, 2},
     {"FBK-IN",     3, 4, 2, 3},
     {"IF FIL1",    5, 6, 0, 1},
     {"IF FIL2",    5, 6, 1, 2},
     {"IF FIL3",    5, 6, 2, 3},
     {"OFF",        6, 7, 0, 1},
     {"PRE-AMP  1", 6, 7, 1, 2},
     {"PRE-AMP  2", 6, 7, 2, 3},
     {"ATT",        6, 7, 3, 4},
     {" 7.003kHz",  8, 9, 0, 1},
     {" 7.026kHz",  8, 9, 1, 2},
     {"10.118kHz",  8, 9, 2, 3},
     {"14.058kHz",  8, 9, 3, 4},
     {"18.085kHz",  8, 9, 4, 5},
     {"21.058kHz",  8, 9, 5, 6},
     {"24.908kHz",  8, 9, 6, 7},
     {"28.058kHz",  8, 9, 7, 8},
    };

    GtkWidget *window;
    GtkWidget *button;
    GtkWidget *table;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "IC-7410 Rig Control");
    gtk_signal_connect (GTK_OBJECT (window), "delete_event", GTK_SIGNAL_FUNC (delete_event), NULL);
    gtk_container_set_border_width (GTK_CONTAINER (window), 20);

    table = gtk_table_new(nx, ny, TRUE);
    gtk_container_add (GTK_CONTAINER (window), table);

    for(int i=0;i<nbutton;i++) {
      button = gtk_button_new_with_label (mybuttons[i].name);
      gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (callback),
                          (gpointer) mybuttons[i].name);
      gtk_table_attach_defaults(GTK_TABLE(table), button, 
                                mybuttons[i].left_attach, mybuttons[i].right_attach,
                                mybuttons[i].top_attach,  mybuttons[i].bottom_attach);
      gtk_widget_show (button);
    }

    button = gtk_button_new_with_label ("Quit");
    gtk_signal_connect (GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC (delete_event), NULL);
    gtk_table_attach_defaults (GTK_TABLE(table), button, 1, 3, ny-3, ny-1);
    gtk_widget_show (button);

    gtk_widget_show (table);
    gtk_widget_show (window);

    gtk_main ();

    return 0;
}

I must add some sliders for AF volume, RF power, etc.

GTK+ and IC-7410 (2)

gtk6

First you need to arrange buttons, each of which has some functionality.

% grep table myprog.c

GtkWidget *table;
table = gtk_table_new (4, 8, TRUE);
gtk_container_add (GTK_CONTAINER (window), table);
gtk_table_attach_defaults (GTK_TABLE(table), button, 0, 2, 0, 3);
gtk_table_attach_defaults (GTK_TABLE(table), button, 4, 5, 1, 3);
gtk_table_attach_defaults (GTK_TABLE(table), button, 6, 7, 2, 3);
gtk_table_attach_defaults (GTK_TABLE(table), button, 1, 6, 4, 5);
gtk_widget_show (table);

Here I used GtkTable to arrange the buttons two dimensionally.

gtk5

You can map an image onto a button to give better impressions.

% ./myprog

CW was pressed
CW-REVERSE was pressed
SSB was pressed

In callback functions, only a g_print line is currently included, which should be replaced to send a command to IC-7410.

GTK+ and IC-7410

gtk

So something is wrong with grig when it controls IC-7410. I might trace the source code of either grig or Hamlib, but another possibility is to write your own control program for the rig you have. It does not have to be generic, so the program and its user interface can be simple and can fit your own needs. For example, I do not need any functionality not relating to CW, such as VOX gain and COMP, etc.

CWKeyboard

I can use a charater based user design like I did in my previous article CW Keybord, but I think this time I will use some GUI.

gtk2

I have played with OpenGL, GLUT, WebGL, etc., but is it my first time to use GTK+?