CW Keyboard (2)

cwkb1

Minor improvement to my old program adding menus for speed change and fixed messages.

cwkb2

The following is the source code.

/* file name = ic7410.c */
/* % gcc ic7410.c -o ic7410 -lncurses */
#include <ncurses.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#define  ENTER      10
#define  ESCAPE     27
#define  NROWMIN    30
#define  NCOLMIN    80
#define  BUFSIZE  1024
#define  BAUDRATE B19200
#define  MYRIG    "/dev/ttyUSB0"

static int speed[]={15,20,25,30,35,40,45,50};
static int nspeed =sizeof(speed) / sizeof(speed[0]);

static char *text_array[]={
  "cq cq cq de jh1ood jh1ood jh1ood k ",
  "de jh1ood jh1ood jh1ood k ",
  "qrz? de jh1ood k ",
  "ur 5nn ^bk ",
  "73 tu e e "};
static int ntext =sizeof(text_array) / sizeof(text_array[0]);

int fd=-1;


void init_curses()
{
    initscr    ();
    start_color();
    noecho     ();
    raw        ();
    keypad     (stdscr, TRUE);
    scrollok   (stdscr, TRUE);
    curs_set   (0);
    init_pair  (1,COLOR_WHITE,COLOR_BLUE);
    init_pair  (2,COLOR_BLUE,COLOR_WHITE);
    init_pair  (3,COLOR_RED,COLOR_WHITE);
    init_pair  (4,COLOR_BLACK,COLOR_GREEN);
}

void draw_menubar(WINDOW *menubar)
{
    wbkgd(menubar,COLOR_PAIR(2));
    waddstr(menubar," WPM ");
    wattron(menubar,COLOR_PAIR(3));
    waddstr(menubar,"(F1)");
    wattroff(menubar,COLOR_PAIR(3));
    wmove(menubar,0,20);
    waddstr(menubar," Messages ");
    wattron(menubar,COLOR_PAIR(3));
    waddstr(menubar,"(F2)");
    wattroff(menubar,COLOR_PAIR(3));
}

WINDOW **draw_menu1(int start_col)
{
    int i;
    WINDOW **items;
    items=(WINDOW **)malloc((nspeed+1)*sizeof(WINDOW *));

    items[0]=newwin(nspeed+2,9,1,start_col);
    wbkgd(items[0],COLOR_PAIR(2));
    box(items[0],ACS_VLINE,ACS_HLINE);
    for (i=1;i<=nspeed;i++) {
    	items[i]=subwin(items[0],1,7,i+1,start_col+1);
        wprintw(items[i],"%2d WPM",speed[i-1]);
    }
    wbkgd(items[1],COLOR_PAIR(1));
    wrefresh(items[0]);
    return items;
}

WINDOW **draw_menu2(int start_col)
{

    int i;
    WINDOW **items;
    items=(WINDOW **)malloc((ntext+1)*sizeof(WINDOW *));

    items[0]=newwin(ntext+2,49,1,start_col);
    wbkgd(items[0],COLOR_PAIR(2));
    box(items[0],ACS_VLINE,ACS_HLINE);
    for (i=1;i<=ntext;i++) {
    	items[i]=subwin(items[0],1,47,i+1,start_col+1);
        wprintw(items[i],"%s",text_array[i-1]);
    }
    wbkgd(items[1],COLOR_PAIR(1));
    wrefresh(items[0]);
    return items;
}

void delete_menu(WINDOW **items,int count)
{
    int i;
    for (i=0;i<count;i++)
        delwin(items[i]);
    free(items);
}

int scroll_menu(WINDOW **items,int count)
{
    int key;
    int selected=0;
    while (1) {
        key=getch();
        if (key==KEY_DOWN || key==KEY_UP) {
            wbkgd(items[selected+1],COLOR_PAIR(2));
            wnoutrefresh(items[selected+1]);
            if (key==KEY_DOWN) {
                selected=(selected+1) % count;
            } else {
                selected=(selected+count-1) % count;
            }
            wbkgd(items[selected+1],COLOR_PAIR(1));
            wnoutrefresh(items[selected+1]);
            doupdate();
        } else if (key==ESCAPE) {
            return -1;
        } else if (key==ENTER) {
            return selected;
        }
    }
}

void send_cw(char* text);

/* IC-7410 key speed set */
/* the parameter 0-255 relates to 6wpm-48wpm */

void send_keyspeed(int wpm) {
  static char output_ks [9] =
   {0xfe, 0xfe, 0x80, 0x00, 0x14, 0x0c,
    0x01, 0x28, /* from 0x00,0x00 to 0x02,0x55 */
    0xfd};
  int param, p0, p1, p2;

  if(wpm <  6) wpm =  6;
  if(wpm > 48) wpm = 48;
  param = 255.0 * ((double)wpm - 6.0) / (48.0 - 6.0) + 0.0;
  p0 =  param      % 10;
  p1 = (param/ 10) % 10;
  p2 = (param/100) % 10;
  output_ks[6] = p2;
  output_ks[7] = 16*p1+p0;
/* because param=123 goes as 0x01, 0x23 (BCD) */
  write(fd, output_ks, 9);
}

void send_cw(char* text) {       /* IC-7410 send CW */
  static char output [BUFSIZE] = {0xfe, 0xfe, 0x80, 0x00, 0x17};
  char *p;
  int  count;

  count=0; p=output+5;
  while((*p++ = *text++)) {
    count++;
    if(count == 30) {            /* IC-7410 max CW text length */
      *p = 0xfd;                 /* IC-7410 postamble */
      write(fd, output, 5+count+1);
      count=0; p=output+5;
    }
  }
  if(count) {
    *(--p) = 0xfd;               /* replace zero with postamble */
    write(fd, output, 5+count+1);
  }
}

void send_stored_text(int id) {  /* use Function Keys */
  send_cw(text_array[id-1]); /* id=1, 2, ... */
  attron(COLOR_PAIR(2)); printw (text_array[id-1]); attroff(COLOR_PAIR(2));
}

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);
}

int main(void) {
  int    c, count=0, nrow, ncol, row=0, col=0;
  char   word[BUFSIZE];
  struct termios oldtio;
  int selected_item;
  WINDOW *menubar,*messagebar, **menu_items;

/* IC-7410 USB I/F initialize */
  fd = open(MYRIG, O_RDWR | O_NOCTTY);
  if (fd < 0) {
    fprintf(stderr,"Error: can not open %s \n", MYRIG);
    return (-1);
  }
  tcgetattr  (fd, &oldtio);
  serial_init();

/* ncurses initialize */
  init_curses();
  getmaxyx   (stdscr,nrow,ncol);
  if(nrow<NROWMIN || ncol < NCOLMIN) {
     row=1; col=0; move(row, col);
     printw("Current Window size is %d rows, %d columns. \n\n"
            ,nrow, ncol);
     printw("Please make the Window size greater than %d rows, %d columns. \n"
            ,NROWMIN, NCOLMIN);
     printw("Hit anykey.. \n");
     getch();
     endwin();
     return EXIT_FAILURE;
  }
  bkgd(COLOR_PAIR(4));
  menubar   =subwin(stdscr,1,40,0, 0);
  messagebar=subwin(stdscr,1,10,0,ncol-10); /* 10chars */
  wattron(messagebar, COLOR_PAIR(2));
  draw_menubar(menubar);
  row=5; col=0; move(row, col);
  refresh();

  while ( (c=getch()) != 0x04) { /* ^d, EOT */
    switch (c) {
      case KEY_F(1):
            menu_items=draw_menu1(0);
            selected_item=scroll_menu(menu_items,nspeed);
            delete_menu(menu_items,nspeed);
            if (selected_item<0)
                ;
            else {
	        wclear(messagebar);
                wprintw(messagebar,
                  "Now %2d WPM",speed[selected_item]); /* 10chars */
		send_keyspeed(speed[selected_item]);
	    }
            touchwin(stdscr);
            refresh();
	    break;
      case KEY_F(2):
            menu_items=draw_menu2(20);
            selected_item=scroll_menu(menu_items,ntext);
            delete_menu(menu_items,ntext);
            if (selected_item<0)
                ;
            else
		send_stored_text(selected_item+1);
            touchwin(stdscr);
            refresh();
	    break;
      case KEY_BACKSPACE:
        if(count) {              /* only within a word */
          getyx  (stdscr, row, col);
          count--; col--;        /* only within the same line */
          mvaddch(row, col, ' ');
          move   (row, col);     /* cursor goes back */
        }
        break;
      default:
        if(isprint(c)) {
          addch(c);
          word[count++] = c;
        }
        break;
    }

    getyx(stdscr, row, col);
    if(c == 0x0a || (c==' ' && (col >= ncol-10 || col == ncol)))
      printw("\n");
    refresh();

    if(c == 0x0a || c == ' ' || c == '.' || c == ',') {
      word[count] = 0; count=0;
      send_cw(word);
    }
  }

  tcsetattr(fd, TCSANOW, &oldtio); /* reset serial terminal */
  endwin   ();                     /* reset ncurses */
  return EXIT_SUCCESS;
}

Note: The menu part of the code is from
http://www.linuxfocus.org/English/March2002/article233.shtml

IC-7410 Keyspeed

A short program to check the data format for IC-7410 Keyspeed control.

#define MYRIG "/dev/ttyUSB0"
int fd = open(MYRIG, O_RDWR | O_NOCTTY);
output[0] = 0xfe; /* IC-7410 preamble */
output[1] = 0xfe; /* IC-7410 preamble */
output[2] = 0x80; /* IC-7410 CI-V address */
output[3] = 0x00; /* my PC address (any) */
output[4] = 0x14; /* IC-7410 command for set params */
output[5] = 0x0c; /* IC-7410 sub-command for keyspeed inquiry */
output[6] = 0xfd; /* IC-7410 postamble */
outputcount = 7; /* command length */
writecount = write(fd, &output, outputcount);

This is the inquiry and its response with different settings of the keyspeed volume.

[FE] [FE] [80] [00] [14] [0C] [FD]
[FE] [FE] [00] [80] [14] [0C] [00] [01] [FD] <- volume at min. ( 6 wpm)
[FE] [FE] [00] [80] [14] [0C] [01] [28] [FD] <- volume at center
[FE] [FE] [00] [80] [14] [0C] [02] [54] [FD] <- volume at max. (48 wpm)

Set several predefined keyspeed through the function keys.

void send_keyspeed(int wpm) { /* IC-7410 key speed */
static char output_ks [9] =
{0xfe, 0xfe, 0x80, 0x00, 0x14, 0x0c,
0x01, 0x28, /* from 0x00,0x00 to 0x02,0x55 */
0xfd};
switch(wpm) {
case 1 : output_ks[6] = 0x00; output_ks[7] = 0x64;break;
case 2 : output_ks[6] = 0x00; output_ks[7] = 0x96;break;
case 3 : output_ks[6] = 0x01; output_ks[7] = 0x28;break;
case 4 : output_ks[6] = 0x01; output_ks[7] = 0x92;break;
}
write(fd, output_ks, 9);
}

By sending a series of dots at each speed, the element length is obtained by observing the waveform of the audio signal, and WPM is obtained by using the equation: WPM=1200/element_length [mS].

keyspeed

The measured curve (in red) differs slightly from a liner interpolation (in green) by the two points suggested by the manual, (0: 6 wpm, 255: 48 wpm). In either case, the parameters for {20|25|30|35} wpm shall be around {83, 112, 141, 170}, respectively.

So, in conclusion, a new version of the source code for CW Keybord is:

/* file name = ic7410m.c */
/* % gcc ic7410m.c -o ic7410m -lncurses */
#include <ncurses.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>
#include <fcntl.h>
#define BUFSIZE 4096
#define BAUDRATE B19200
#define MYRIG "/dev/ttyUSB0"

int fd=-1;

void send_cw(char* text);

void send_keyspeed(int wpm) { /* IC-7410 key speed */
static char output_ks [9] =
{0xfe, 0xfe, 0x80, 0x00, 0x14, 0x0c,
0x01, 0x28, /* from 0x00,0x00 to 0x02,0x55 */
0xfd};

switch(wpm) { /* 20, 25, 30, 35 wpm */
case 1 : output_ks[6] = 0x00; output_ks[7] = 0x83;break;
case 2 : output_ks[6] = 0x01; output_ks[7] = 0x12;break;
case 3 : output_ks[6] = 0x01; output_ks[7] = 0x41;break;
case 4 : output_ks[6] = 0x01; output_ks[7] = 0x70;break;
}

write(fd, output_ks, 9);
}

void send_cw(char* text) { /* IC-7410 send CW */
static char output [BUFSIZE] = {0xfe, 0xfe, 0x80, 0x00, 0x17};
char *p;
int count;

count=0; p=output+5;
while(*p++ = *text++) {
count++;
if(count == 30) { /* IC-7410 max CW text length */
*p = 0xfd; /* IC-7410 postamble */
write(fd, output, 5+count+1);
count=0; p=output+5;
}
}
if(count) {
*(--p) = 0xfd; /* replace zero with postamble */
write(fd, output, 5+count+1);
}
}

void send_stored_text(int id) { /* use Function Keys */
static char *text_array[]={
"cq cq cq de jh1ood jh1ood jh1ood k ", /* F1 */
"qrz? de jh1ood k ", /* F2 */
"ur 5nn ^bk ", /* F3 */
"73 tu e e "}; /* F4 */

send_cw(text_array[id-1]); /* id=1, 2, ... */
attron(COLOR_PAIR(2)); printw (text_array[id-1]); attroff(COLOR_PAIR(2));
}

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);
}

int main (void) {
int c, i, count=0, nrow, ncol, row=0, col=0;
char word[BUFSIZE];
struct termios oldtio;

fd = open(MYRIG, O_RDWR | O_NOCTTY);
if (fd < 0) {
fprintf(stderr,"Error: can not open %s n", MYRIG);
return (-1);
}
tcgetattr (fd, &oldtio);
serial_init();

initscr (); /* ncurses init */
raw ();
noecho ();
keypad (stdscr, TRUE);
scrollok (stdscr, TRUE);
getmaxyx (stdscr,nrow,ncol);
start_color();
init_pair (1, COLOR_RED , COLOR_BLACK);
init_pair (2, COLOR_YELLOW, COLOR_BLACK);
attron (COLOR_PAIR(1)); printw("CW keyboard.."); attroff(COLOR_PAIR(1));
row=2; col=0; move(row, col);
refresh ();

while ( (c=getch()) != 0x04) { /* EOT */
switch (c) {
case KEY_F(1): send_stored_text(1); break;
case KEY_F(2): send_stored_text(2); break;
case KEY_F(3): send_stored_text(3); break;
case KEY_F(4): send_stored_text(4); break;
case KEY_F(5): send_keyspeed (1); break;
case KEY_F(6): send_keyspeed (2); break;
case KEY_F(7): send_keyspeed (3); break;
case KEY_F(8): send_keyspeed (4); break;
case KEY_BACKSPACE:
if(count) { /* only within a word */
getyx (stdscr, row, col);
count--; col--; /* only within the same line */
mvaddch(row, col, ' ');
move (row, col); /* cursor goes back */
}
break;
default:
if(isprint(c)) {
addch(c);
word[count++] = c;
}
break;
}

getyx(stdscr, row, col);
if(c == 0x0a || c==' ' && col >= ncol-10 || col == ncol)
printw("n");
refresh();

if(c == 0x0a || c == ' ' || c == '.' || c == ',') {
word[count] = 0; count=0;
send_cw(word);
}
}

tcsetattr(fd, TCSANOW, &oldtio); /* reset serial terminal */
endwin (); /* reset ncurses */
return EXIT_SUCCESS;
}