IC-7410 CW Tuning Indicator (2)

click_ani2

You are listening on 7017.290kHz, and your current CW pitch of 600Hz is shown on the waterfall as a vertical black bar. (Please click the image to view the full sized version.)

You find two signals on the waterfall, one lower and another higher than the CW pitch. You click on the lower one, and the rig is now tuned on 7017.002kHz so that the designed signal can now be heard as 600Hz.

I am now writing a new page, IC-7410 Rig Control. See the new top menu.

IC-7410 CW Tuning Indicator

pitch_ani

This is a practical version of the CW tuning indicator for IC-7410. You can select the CW pitch of your choice between 450Hz and 750Hz using one of the radio buttons on the panel. The selected pitch is shown on the display as a dark green bar.

You can tune manually so that the peak of the bar graph coincides with the dark green bar.

The next thing to do is to click on the display either on the bar graph or on the waterfall to tune in on that frequency.

CW Tuning Indicator (3)

fft_ani

This looks better?

waterfall2

Waterfall pseudo-color encoded, but not artistically.

cpuFFT

The CPU load gets a little bit higher due to FFT and extra drawing operations.

waterfall4

CW Tuning Indicator (2)

fft1

The green trace is the audio signal waveform, and the red trace is the FFTed version. If you change the CW pitch or the tuning, the peak position moves accordingly.

So this could be a CW tuning indicator in its primitive form.

% gcc -Wall -std=c99 test53.c -o test53 -lm -lasound -lfftw3 `pkg-config --cflags --libs gtk+-2.0`

The library FFTW is used here.

IC-7410 Rig Control Program with Audio I/F

gtkAlsa

Now we combine two programs, namely: IC-7410 Rig Control Program using gtk+ and IC-7410 Audio I/F program using ALSA.

The ALSA part is responsible for capturing the audio signal from IC-7410 and writing the data to stdout, which is pipelined to an audio player program, aplay.

waveform2

Or you can display the waveform on the screen.

IC-7410 Audio Interface and ALSA (3)

The original pcm.c has 925 lines to send a sine wave with various transfer methods. Here is a simplified version only to support asynchronous notification, and instead of playing a waveform, the audio signal is captured from the device.

capture

The source program is compiled, the audio signal from IC-7410 (hw:2,0) is captured and stored in a file, and the file is played back on a speaker (hw:1,3).

/* file name = pcm_capture3.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include "asoundlib.h"
#include <sys/time.h>
#include <math.h>

static char *device             = "hw:2,0";	/* playback device is IC-7410*/
static unsigned int rate        = 44100;	/* stream rate */
static unsigned int channels    = 2;		/* count of channels */
static int byte_per_sample      = 2;		/* 16 bit format */
static unsigned int buffer_time = 500000;	/* ring buffer length in us */
static unsigned int period_time = 100000;	/* period time in us */
static int resample             = 0;		/* enable alsa-lib resampling */
static int period_event         = 0;		/* produce poll event after each period */

static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;

static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params)
{
	unsigned int      rrate;
	snd_pcm_uframes_t size;
	int               err, dir;

	/* choose all parameters */
	err = snd_pcm_hw_params_any(handle, params);
	if (err < 0) {
		fprintf(stderr,"Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
		return err;
	}

	/* set hardware resampling */
	err = snd_pcm_hw_params_set_rate_resample(handle, params, resample);
	if (err < 0) {
		fprintf(stderr,"Resampling setup failed for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the interleaved read/write format */
	err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
	if (err < 0) {
		fprintf(stderr,"Access type not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the sample format */
	err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16);
	if (err < 0) {
		fprintf(stderr,"Sample format not available for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* set the count of channels */
	err = snd_pcm_hw_params_set_channels(handle, params, channels);
	if (err < 0) {
		fprintf(stderr,"Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err));
		return err;
	}

	/* set the stream rate */
	rrate = rate;
	err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
	if (err < 0) {
		fprintf(stderr,"Rate %iHz not available for playback: %s\n", rate, snd_strerror(err));
		return err;
	}
	if (rrate != rate) {
		fprintf(stderr,"Rate doesn't match (requested %iHz, get %iHz)\n", rate, err);
		return -EINVAL;
	}

	/* set the buffer time */
	err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);
	if (err < 0) {
		fprintf(stderr,"Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_buffer_size(params, &size);
	if (err < 0) {
		fprintf(stderr,"Unable to get buffer size for playback: %s\n", snd_strerror(err));
		return err;
	}
	buffer_size = size;

	/* set the period time */
	err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);
	if (err < 0) {
		fprintf(stderr,"Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
		return err;
	}
	err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
	if (err < 0) {
		fprintf(stderr,"Unable to get period size for playback: %s\n", snd_strerror(err));
		return err;
	}
	period_size = size;

	/* write the parameters to device */
	err = snd_pcm_hw_params(handle, params);
	if (err < 0) {
		fprintf(stderr,"Unable to set hw params for playback: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
{
	int err;

	/* get the current swparams */
	err = snd_pcm_sw_params_current(handle, swparams);
	if (err < 0) {
		fprintf(stderr,"Unable to determine current swparams for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* start the transfer when the buffer is almost full: */
	/* (buffer_size / avail_min) * avail_min */
	err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);
	if (err < 0) {
		fprintf(stderr,"Unable to set start threshold mode for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* allow the transfer when at least period_size samples can be processed */
	/* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
	err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
	if (err < 0) {
		fprintf(stderr,"Unable to set avail min for playback: %s\n", snd_strerror(err));
		return err;
	}

	/* enable period events when requested */
	if (period_event) {
		err = snd_pcm_sw_params_set_period_event(handle, swparams, 1);
		if (err < 0) {
			fprintf(stderr,"Unable to set period event: %s\n", snd_strerror(err));
			return err;
		}
	}

	/* write the parameters to the playback device */
	err = snd_pcm_sw_params(handle, swparams);
	if (err < 0) {
		fprintf(stderr,"Unable to set sw params for playback: %s\n", snd_strerror(err));
		return err;
	}
	return 0;
}

static void async_callback(snd_async_handler_t *ahandler)
{
	snd_pcm_t                 *handle  = snd_async_handler_get_pcm(ahandler);
	signed short              *samples = snd_async_handler_get_callback_private(ahandler);
	snd_pcm_sframes_t          avail;
	int                        err, write_count, wc;
	
	avail = snd_pcm_avail_update(handle);
	while (avail >= period_size) {
		err = snd_pcm_readi (handle, samples, period_size);
		if (err < 0) {
			fprintf(stderr,"Write error: %s\n", snd_strerror(err));
			exit(EXIT_FAILURE);
		}
		if (err != period_size) {
			fprintf(stderr,"Write error: written %i expected %li\n", err, period_size);
			exit(EXIT_FAILURE);
		}

		write_count = period_size * channels * byte_per_sample;
		wc = write(1, samples, write_count);
		if(wc != write_count) {
			fprintf(stderr,"file write error. \n");
		}

		avail = snd_pcm_avail_update(handle);
	}
}

static int async_loop(snd_pcm_t *handle, signed short *samples)
{
	snd_async_handler_t       *ahandler;
	int                        err;

	err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, samples);
	if (err < 0) {
		fprintf(stderr,"Unable to register async handler\n");
		exit(EXIT_FAILURE);
	}

	if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) {
		err = snd_pcm_start(handle);
		if (err < 0) {
			fprintf(stderr,"Start error: %s\n", snd_strerror(err));
			exit(EXIT_FAILURE);
		}
	}

	while (1) {
		sleep(1);
	}
}

int main(int argc, char *argv[])
{
	snd_pcm_t              *handle;
	snd_pcm_hw_params_t    *hwparams;
	snd_pcm_sw_params_t    *swparams;
	signed short           *samples;
	int                     err;

	snd_pcm_hw_params_alloca(&hwparams);
	snd_pcm_sw_params_alloca(&swparams);

	fprintf(stderr,"capture device = %s, rate = %i Hz, %i channels\n", device, rate, channels);

	if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
		fprintf(stderr,"Capture open error: %s\n", snd_strerror(err));
		return 0;
	}
	
	if ((err = set_hwparams(handle, hwparams)) < 0) {
		fprintf(stderr,"Setting of hwparams failed: %s\n", snd_strerror(err));
		exit(EXIT_FAILURE);
	}
	if ((err = set_swparams(handle, swparams)) < 0) {
		fprintf(stderr,"Setting of swparams failed: %s\n", snd_strerror(err));
		exit(EXIT_FAILURE);
	}

	samples = malloc(period_size * channels * byte_per_sample);
	if (samples == NULL) {
		fprintf(stderr,"No enough memory\n");
		exit(EXIT_FAILURE);
	}
	
	err = async_loop(handle, samples);
	if (err < 0) fprintf(stderr,"Transfer failed: %s\n", snd_strerror(err));

	free(samples);
	snd_pcm_close(handle);
	return 0;
}

Recording Computer Playback

Suppose you are listening to something on your PC, and you wish to record that sound. Here is a solution.

volume1

The sound is being played back on one of the output devices of your choice, an HDMI audio output in this case.

volume2

You can use, say, Audacity, for recording.

volume3

Use PulseAudio Volume Control (pavucontrol) to select the device you wish to capture from, which is the device you are playing the sound with.

http://manual.audacityteam.org/o/man/tutorial_recording_computer_playback_on_linux.html

IC-7410 Audio Interface and ALSA (2)

pcm2

For the playback, IC-7410 seems to accept the audio data at least with three different sample rates, namely 32kHz, 44.1kHz and 48kHz.

pcm3

It does not work with these sample rates.

You can hear the sound with the Monitor function of IC-7410, if you set the “DATA OFF MOD (menu item no. 40)” to “USB”, and push the “TRANSMIT SWITCH” on, preferably using a dummy load.

% cp ./alsa-lib-1.0.28/test/pcm.c pcm2.c
% gcc pcm2.c -o pcm -lasound
(you need "asoundlib.h", which you can find in "alsa-lib-1.0.28/include".)

IC-7410 Audio Interface and ALSA

alsalogo

It might be good if I can get audio signals from IC-7410 in my rig control program. I used to use APIs provided by JACK, but this time I am trying with ALSA.

async

This could be a starting point for a beginner, so let’s see an example, pcm.c.

/*
 *   Transfer method - asynchronous notification
 */
struct async_private_data {
        signed short *samples;
        snd_pcm_channel_area_t *areas;
        double phase;
};
static void async_callback(snd_async_handler_t *ahandler)
{
        snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler);
        struct async_private_data *data = snd_async_handler_get_callback_private(ahandler);
        signed short *samples = data->samples;
        snd_pcm_channel_area_t *areas = data->areas;
        snd_pcm_sframes_t avail;
        int err;
        
        avail = snd_pcm_avail_update(handle);
        while (avail >= period_size) {
                generate_sine(areas, 0, period_size, &data->phase);
                err = snd_pcm_writei(handle, samples, period_size);
                if (err < 0) {
                        printf("Write error: %s\n", snd_strerror(err));
                        exit(EXIT_FAILURE);
                }
                if (err != period_size) {
                        printf("Write error: written %i expected %li\n", err, period_size);
                        exit(EXIT_FAILURE);
                }
                avail = snd_pcm_avail_update(handle);
        }
}
static int async_loop(snd_pcm_t *handle,
                      signed short *samples,
                      snd_pcm_channel_area_t *areas)
{
        struct async_private_data data;
        snd_async_handler_t *ahandler;
        int err, count;
        data.samples = samples;
        data.areas = areas;
        data.phase = 0;
        err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data);
        if (err < 0) {
                printf("Unable to register async handler\n");
                exit(EXIT_FAILURE);
        }
        for (count = 0; count < 2; count++) {
                generate_sine(areas, 0, period_size, &data.phase);
                err = snd_pcm_writei(handle, samples, period_size);
                if (err < 0) {
                        printf("Initial write error: %s\n", snd_strerror(err));
                        exit(EXIT_FAILURE);
                }
                if (err != period_size) {
                        printf("Initial write error: written %i expected %li\n", err, period_size);
                        exit(EXIT_FAILURE);
                }
        }
        if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) {
                err = snd_pcm_start(handle);
                if (err < 0) {
                        printf("Start error: %s\n", snd_strerror(err));
                        exit(EXIT_FAILURE);
                }
        }
        /* because all other work is done in the signal handler,
           suspend the process */
        while (1) {
                sleep(1);
        }
}

Looks reasonable, isn’t it? So shall we begin?