Simplified

ncurses3

Yet another minor improvement. I suppose this version of the code is very readable. It might be better if I could make it to be less than 100 lines. Adding bells and whistles is relatively easy, the difficult part is making it simple.

/* file name = ic-7410_cw_keyboard_ncurses3.c */
/* % gcc source_code.c -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) {       /* 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 ",
  "qrz? de jh1ood k "};

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

Stored Messages

ncurses2

Minor improvement.

/* file name = ic-7410_cw_keyboard_ncurses2.c */
#include <ncurses.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/wait.h>
#define  BUFSIZE 4096
#define  BAUDRATE B19200
#define  MYRIG "/dev/ttyUSB0"

void send_cw(int fd, char* message) {
  static char output [BUFSIZE] = {0xfe, 0xfe, 0x80, 0x00, 0x17};
  char *p;
  int  count;

  count = 0; p=output+5;
  while(*p++ = *message++) {
    count++;
    if(count == 30) { /* message can not exceed 30 chars */
      *p = 0xfd; /* IC-7410 postamble */
      write(fd, output, 5+count+1);
      count=0; p=output+5;
    }
  }
  if(count) {
    *(--p) = 0xfd; /* replace zero with IC-7410 postamble */
    write(fd, &output, 5+count+1);
  }
}

void serial_init(int fd) {
  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 fd, c, i, nread, count=0;
  int nrow, ncol, row=0, col=0;
  char message[BUFSIZE], output [BUFSIZE];
  struct termios oldtio;

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

  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(fd);

  while ( (c=getch()) != 0x04) { /* EOT */
    switch (c) {
      case KEY_F(1):
        send_cw(fd, "cq de jh1ood k ");
        attron(COLOR_PAIR(2));
        printw("CQ DE JH1OOD K ");
        attroff(COLOR_PAIR(2));
        getyx(stdscr, row, col); move(row, col);
        break;
      case KEY_F(2):
        send_cw(fd, "qrz? de jh1ood k ");
        attron(COLOR_PAIR(2));
        printw("QRZ? DE JH1OOD K ");
        attroff(COLOR_PAIR(2));
        getyx(stdscr, row, col); move(row, col);
        break;
      case KEY_BACKSPACE:
        count--; col--; mvaddch(row, col, ' '); move(row, col);
        break;
      case 0x0a: /* LF */
        row++; col = 0;
        if(row >= nrow) {wscrl(stdscr,1);row--;}
        move(row, col);
        break;
      default:
        mvaddch(row, col, c);
        col++;
        message[count++] = c;
        break;
    }

    if(col >= ncol-10 && c==' ' || col == ncol){
      row++; col= 0;
      if(row >= nrow) {wscrl(stdscr,1);row--;}
      move(row, col);
    }

    if(c == ' ' || c == '.' || c == ',' || c == 0x0a) {
      message[count] = 0;
      count=0;
      send_cw(fd, message);
    }
  }
  tcsetattr(fd, TCSANOW, &oldtio);
  endwin();
  return EXIT_SUCCESS;
}

Ncurses

ncurses

Ncurses is a library of functions that manages an application’s display on character-cell terminals.

[1] http://www.tldp.org/HOWTO/NCURSES-Programming-HOWTO/index.html

[2] http://invisible-island.net/ncurses/ncurses-intro.html

Another version of CW keyboard program using ncurses.

/* file name = ic-7410_cw_keyboard_ncurses.c */
#include <ncurses.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/wait.h>
#define BUFSIZE 4096
#define BAUDRATE B19200
#define MYRIG "/dev/ttyUSB0"

struct termios saved_attributes;
static int fd = -1;

void serial_init(int fd) {
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) {
char c;
int i, nread, count=0, outputcount;
char message[BUFSIZE];
char output [BUFSIZE];
struct termios oldtio, newtio;
int nrow, ncol, row=0, col=0;

initscr();
raw();
noecho();
getmaxyx(stdscr,nrow,ncol);
start_color();
init_pair(1, COLOR_RED, COLOR_BLACK);
attron(COLOR_PAIR(1));
printw("CW keyboard.. n");
attroff(COLOR_PAIR(1));
row=1; col=0;
scrollok(stdscr, TRUE);
refresh();

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(fd);

while (1) {
c=getch();
mvaddch(row,col,c);
col++;
if(col >= ncol*0.8 && c==' '){
row++; col=0;
}

if(c == 0x0a || c == 0x0d) {
row++; col=0;
}

if(row == nrow) {
row=nrow-1;
}

message[count++]=c;
if (c == '\004') { /* C-d */
break;
} else {
if(c == 0x07 || c == 0x7f) { /* backspace or delete */
count-=2; /* forget both backspace and char */
col -=2;
}
if(c == ' ' || c == '.' || c == '\012') { /* newline */
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] = 0x17; /* IC-7410 command for CW message */
for(i=0;i < count;i++) {
output[5+i] = message[i];
}
output[5+count] = 0xfd; /* IC-7410 postamble */
outputcount = count+6; /* total length = CW message + (5+1) */
write(fd, &output, outputcount);
count=0;
}
}
}
tcsetattr(fd, TCSANOW, &oldtio);
endwin();
return EXIT_SUCCESS;
}

From text

We can also send Morse code not only from a keyboard but from a text.

% echo "cq de jh1ood k" | ic-7410_cw_keyboard
% ic-7410_cw_keyboard < message1.txt

At EOF, we need to wait a little bit before exiting so that all the text is transmitted to IC-7410. This is not very smart, so it might be better to check the ack from IC-7410 instead of just waiting for a fixed period.

diff -c ic-7410_cw_keyboard.c ic-7410_cw_keyboard_from_text.c
    while (1)
      {
        nread = read (STDIN_FILENO, &c, 1);
+       if(nread == 0) { sleep(2); return EXIT_SUCCESS;}
        message[count++]=c;
        if (c == '\004') { /* C-d */
          break;

Noncanonical

A very simple CW keyboard program is presented. No thread programming, using only noncanonical input mode to catch your key strokes one by one. You enter your message from a keyboard, and as soon as you hit a space or a period or an enter key, a word is sent to IC-7410. Having six bytes of overhead for each word does not sound very efficient, but since IC-7410 can only accept up to 30 characters at a time, this is a good compromise.

[1] http://www.gnu.org/software/libc/manual/html_node/Noncanon-Example.html

[2] http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html

/* file name = ic-7410_cw_keyboard.c */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <sys/wait.h>
#define BUFSIZE 4096
#define BAUDRATE B19200
#define MODEMDEVICE "/dev/ttyUSB0"

struct termios saved_attributes;
static int fd = -1;

void serial_init(int fd) {
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);
}

void reset_input_mode (void) {
tcsetattr (STDIN_FILENO, TCSANOW, &saved_attributes);
}

void set_input_mode (void) {
struct termios tattr;
char *name;

/* Save the terminal attributes so we can restore them later. */
tcgetattr (STDIN_FILENO, &saved_attributes);
atexit (reset_input_mode);

/* Set the funny terminal modes. */
tcgetattr (STDIN_FILENO, &tattr);
tattr.c_lflag &= ~ICANON; /* Clear ICANON. */
tattr.c_lflag |= ECHO; /* Use ECHO. */
tattr.c_cc[VMIN ] = 1;
tattr.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &tattr);
}

int main (void) {
char c;
int i, nread, count=0, outputcount;
char message[BUFSIZE];
char output [BUFSIZE];
struct termios oldtio, newtio;

if (isatty (STDIN_FILENO)) {
fprintf (stderr, "Enter your message.. n");
}
set_input_mode ();

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

tcgetattr(fd, &oldtio);
memset(&newtio, 0, sizeof(newtio));
serial_init(fd);

while (1)
{
nread = read (STDIN_FILENO, &c, 1);
message[count++]=c;
if (c == '\004') /* C-d */
break;
else
if(c == ' ' || c == '.' || c == '\012') {
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] = 0x17; /* IC-7410 command for CW message */
for(i=0;i < count;i++) {
output[5+i] = message[i];
}
output[5+count] = 0xfd; /* IC-7410 postamble */
outputcount = count+6; /* total length = CW message + (5+1) */
write(fd, &output, outputcount);
count=0;
}
}
return EXIT_SUCCESS;
}

USB Sound

The USB I/F of IC-7410 can also be used for audio recording and playback.

usbSound
usbSound2

A GUI program Audacity is used here, but if your prefer to use CLIs, do as in the following way.

% arecord -l
**** List of CAPTURE Hardware Devices ****
card 1: CODEC [USB Audio CODEC], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

% aplay -l
**** List of PLAYBACK Hardware Devices ****
card 1: CODEC [USB Audio CODEC], device 0: USB Audio [USB Audio]
  Subdevices: 1/1
  Subdevice #0: subdevice #0

% arecord -d 5 -r 48000 -f S16_LE -D hw:1,0 &gt; test.wav
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 48000 Hz, Mono

% aplay -D hw:1,0 test.wav
Playing WAVE 'test.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Mono

You can check the maximum sampling frequency by giving some higher values.

% arecord -d 5 -r 96000 -f S16_LE -D hw:1,0 &gt; test.wav
Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 96000 Hz, Mono
Warning: rate is not accurate (requested = 96000Hz, got = 48000Hz)
         please, try the plug plugin

CW Keyboard

Here is a very simple CW keyboard program.

% ic-7410_cw_keyboard
cq de jh1ood k
[FE] [FE] [80] [00] [17] [63] [71] [20] [64] [65] [20] [6A] [68] [31] [6F] [6F] [64] [20] [6B] [0A] [FD]
[FE] [FE] [00] [80] [FB] [FD]

The second line shows the keyboard input. The third line is what is sent to IC-7410 via USB I/F. The forth line is the message from IC-7410 to acknowledge the command.

/* Icom IC-7410 control via serial port */
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;fcntl.h&gt;
#include &lt;string.h&gt;
#include &lt;termios.h&gt;
#include &lt;time.h&gt;
#include &lt;sys/wait.h&gt;
#define BUFFSIZE 256
#define COULDNOTFORK -1
#define FALSE 0
#define TRUE 1

#define BAUDRATE B19200
#define MODEMDEVICE &quot;/dev/ttyUSB0&quot;

volatile int STOP = FALSE;
static   int fd   = -1;

void serial_init(int fd) {
    struct termios tio;

    memset(&amp;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(&amp;tio, BAUDRATE);
    cfsetospeed(&amp;tio, BAUDRATE);
    tcsetattr(fd, TCSANOW, &amp;tio);
}

void receive_process() {
    unsigned char buf[BUFFSIZE];
    char input[BUFFSIZE];
    int count;
    int i;
    int writecount = 0;

    while (FALSE == STOP) {
      memset(&amp;buf, 0, sizeof(buf));
      count = read(fd, &amp;buf, BUFFSIZE);
      if (count &lt; 0) {
        STOP = TRUE;
      } else {
        for(i=0; i &lt; count; i++){
          printf(&quot;[%02X] &quot;, buf[i] &amp; 0x000000FF);
          if ( (int) buf[i] == 0xfd) { /* IC-7410 postamble */
            printf(&quot;n&quot;);;
          }
        fflush( stdout );
        }
      }
    }
}

void send_process(pid_t result_pid) {
    unsigned char input [BUFFSIZE];
    unsigned char output[BUFFSIZE];
    int writecount  = 0;
    int inputcount  = 0;
    int outputcount = 0;
    int i = 0;

    while (1) {
        memset(&amp;input, 13, sizeof(input));
        fgets(input, sizeof(input), stdin);
        fflush(stdin);
        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] = 0x17; /* IC-7410 command for CW message */
        for(i=0;i &lt; BUFFSIZE;i++) {
          if(input[i] == 0) {
            outputcount = i;
             break;
          } else {
           output[5+i] = input[i];
          }
        }
        output[5+outputcount] = 0xfd; /* IC-7410 postamble */
        outputcount += 6; /* total length = CW message + (5+1) */
        writecount = write(fd, &amp;output, outputcount);
        if (writecount &lt; 0) {
            break;
        }
    }
}

int main(void) {
    pid_t result_pid;
    struct termios oldtio, newtio;
    char buf[255];

    fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY);
    if (fd &lt; 0) {
        return (-1);
    }

    tcgetattr(fd, &amp;oldtio);
    memset(&amp;newtio, 0, sizeof(newtio));
    serial_init(fd);
    result_pid = fork();

    if (result_pid == -1) {
        return COULDNOTFORK;
    }

    if (result_pid == 0) {
        receive_process();
    } else {
        send_process(result_pid);
    }
    STOP = TRUE;
    return 0;
}

IC-7410 USB Interface

ic-7410

ICOM IC-7410 has a Type B USB port for audio I/O and for remote control.
[1] http://www.icomamerica.com/en/products/amateur/hf/7410/default.aspx

You can check if IC-7410 is successfully connected to you PC by the following manner.

% dmesg
[  902.112054] usb 4-3: new full-speed USB device number 5 using ohci_hcd
[  902.280364] hub 4-3:1.0: USB hub found
[  902.282377] hub 4-3:1.0: 4 ports detected
[  902.573128] usb 4-3.1: new full-speed USB device number 6 using ohci_hcd
[  902.750271] input: Burr-Brown from TI USB Audio CODEC as /devices/pci0000:00/0000:00:12.1/usb4/4-3/4-3.1/4-3.1:1.3/input/input18
[  902.750468] generic-usb 0003:08BB:2901.0005: input,hidraw3: USB HID v1.00 Device [Burr-Brown from TI USB Audio CODEC ] on usb-0000:00:12.1-3.1/input3
[  902.825135] usb 4-3.2: new full-speed USB device number 7 using ohci_hcd
[  902.937216] cp210x 4-3.2:1.0: cp210x converter detected
[  903.013144] usb 4-3.2: reset full-speed USB device number 7 using ohci_hcd
[  903.119452] usb 4-3.2: cp210x converter now attached to ttyUSB0
% cat /proc/asound/cards
 2 [CODEC          ]: USB-Audio - USB Audio CODEC
                      Burr-Brown from TI USB Audio CODEC at usb-0000:00:12.1-3.1, full speed
% ls -l /dev/snd/by-id
lrwxrwxrwx 1 root root 12 May 25 17:31 usb-Burr-Brown_from_TI_USB_Audio_CODEC-00 -> ../controlC2
% ls -l /dev | grep USB
crw-rw----  1 root dialout 188,   0 May 24 17:53 ttyUSB0

Since the maximum serial communications speed of IC-7410 is 19200 baud, you may need to change the speed of the serial port of your PC.

% stty -F /dev/ttyUSB0
speed 115200 baud; line = 0;
-brkint -imaxbel
% stty -F /dev/ttyUSB0 19200
% echo -e "xfexfex80x00x17x41x42x43x44xfd" > /dev/ttyUSB0

The last line gives you the Morse code “ABCD”, which is represented in hex code as x41x42x43x44.

geom_histogram

This is a histogram of Morse code by JO1FYC.
[1] http://homepage2.nifty.com/jo1fyc/sound/20051010_nikki-32.mp3

dataFrame3Figure 1. Histogram

ggplot(mydata, aes(x=V2, fill=V1)) + geom_histogram(binwidth=50)+facet_grid(V1 ~ .)+xlim(0,3000)

One more from JO1FYC.
[2] http://homepage2.nifty.com/jo1fyc/sound/20010915amenimomakezu.mp3

dataFrame4Figure 2. Histgram 2

Still more from JO1FYC.
[3] http://homepage2.nifty.com/jo1fyc/sound/20010504nikki.MP3

dataFrame5Figure 3. Histogram 3

Yet another from JO1FYC.
[4] http://homepage2.nifty.com/jo1fyc/sound/20010504nikki02.MP3

dataFrame6Figure 4. Histogram 4

Still another from JO1FYC.
[5] http://homepage2.nifty.com/jo1fyc/sound/20010504nikki03.MP3

dataFrame7Figure 5. Histogram 5