Source code: https://github.com/jh1ood/WebSDR
pi@raspberrypi:~/Znodejs $ uname -a Linux raspberrypi 4.9.25-v7+ #994 SMP Fri Apr 28 16:56:00 BST 2017 armv7l GNU/Linux pi@raspberrypi:~/Znodejs $ node --version v6.10.2 pi@raspberrypi:~/Znodejs $ npm --version 3.10.10 pi@raspberrypi:~/Znodejs $ mkfifo myfifo pi@raspberrypi:~/Znodejs $ mkfifo myfifo2
pi@raspberrypi:~/Znodejs $ (./sprig_audio /dev/ttyUSB0 hw:1,0 hw:0,0 > myfifo < myfifo2 &) ; node index.js < myfifo > myfifo2
index.js
// file name = index.js // (c) 2017 JH1OOD/Mike const bufa = new Buffer('fefe80e017' , 'hex'); // preamble const bufz = new Buffer('fd' , 'hex'); // postamble const buf1 = new Buffer('fefe80e0174351fd' , 'hex'); // CQ const buf2 = new Buffer('fefe80e01751525a3ffd' , 'hex'); // QRZ? const buf3 = new Buffer('fefe80e003fd' , 'hex'); // read freq var buf4 = new Buffer('fefe80e0050000000000fd', 'hex'); // send freq var SerialPort = require('serialport'); var serial = new SerialPort( '/dev/ttyUSB0', {baudrate : 19200, parser : SerialPort.parsers.byteDelimiter([ 0xfd ])}); var http = require('http'); var fs = require('fs'); var index = fs.readFileSync(__dirname + '/index.html'); var app = http.createServer(function(req, res) { res.writeHead(200, {'Content-Type' : 'text/html'}); res.end(index); }) .listen(3000); var io = require('socket.io').listen(app); var freqHz = 0; // VFO-A frequency var ndata = 512 + 2048; // waterfall_data[512] + sound_data[2048] var pos = 0; var myarray = new Array(); var mywater = new Array(); var mysound = new Array(); // -- water fall -- process.stdin.on('readable', function() { // from spinor_audio var buf = process.stdin.read(); if (buf !== null) { for(var i=0;i<buf.length;i++) { myarray[pos++] = buf[i]; if (pos == ndata) { for(var j=0;j<512;j++) { // waterfall_data mywater[j] = myarray[j]; } for(var j=0;j<2048;j++) { // sound_data var tmp = myarray[j+512]; if (tmp>128) tmp -=256; mysound [j] = tmp / 128.0; // signed char to -1.0~+1.0 } io.emit('waterfall', mywater); io.emit('sound' , mysound); pos = 0; } } } }); // -- serial for IC-7410 -- serial.on('open', function() { console.error('serial port /dev/ttyUSB0 is opened.'); }); serial.on('data', function(data) { if (!(data[0] == 0xfe & data[1] == 0xfe)) { console.error('-- received serial data format error'); } if (data[2] == 0xe0 & data[3] == 0x80 & data[4] == 0x03) { var f10 = data[5] >> 4 & 0x0f; var f1 = data[5] & 0x0f; var f1k = data[6] >> 4 & 0x0f; var f100 = data[6] & 0x0f; var f100k = data[7] >> 4 & 0x0f; var f10k = data[7] & 0x0f; var f10m = data[8] >> 4 & 0x0f; var f1m = data[8] & 0x0f; var freq = f10m.toString() + f1m.toString() + "," + f100k.toString() + f10k.toString() + f1k.toString() + "." + f100.toString() + f10.toString() + f1 + " kHz"; freqHz = f10m * 10000000 + f1m * 1000000 + f100k * 100000 + f10k * 10000 + f1k * 1000 + f100 * 100 + f10 * 10 + f1; io.emit('freqmsg', 'VFO A: ' + freq); } }); serial.on('error', function(err) { console.error('Error: ', err.message); }) // -- set frequency -- function setfreq(f) { var f10m = Math.floor(f / 10000000); f -= f10m * 10000000; var f1m = Math.floor(f / 1000000); f -= f1m * 1000000; var f100k = Math.floor(f / 100000); f -= f100k * 100000; var f10k = Math.floor(f / 10000); f -= f10k * 10000; var f1k = Math.floor(f / 1000); f -= f1k * 1000; var f100 = Math.floor(f / 100); f -= f100 * 100; var f10 = Math.floor(f / 10); f -= f10 * 10; var f1 = Math.floor(f / 1); var data = new Array( [ 0xfe, 0xfe, 0x80, 0xe0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd ]); buf4[5] = f10 << 4 | f1; buf4[6] = f1k << 4 | f100; buf4[7] = f100k << 4 | f10k; buf4[8] = f10m << 4 | f1m; buf4[9] = 0; serial.write(buf4); } // -- WebSocket -- io.on('connection', function(socket) { socket.on('message1', function() { serial.write(buf1); }); socket.on('message2', function() { serial.write(buf2); }); socket.on('message3', function() { var newfreq = freqHz + 2000; setfreq(newfreq); }); socket.on('message4', function() { var newfreq = freqHz - 2000; setfreq(newfreq); }); socket.on('message5', function() { console.log('0'); // should be stdout to reach sprig_audio }); socket.on('message6', function() { console.log('1'); // should be stdout to reach sprig_audio }); socket.on('message7', function() { console.log('2'); // should be stdout to reach sprig_audio }); socket.on('message8', function() { console.log('3'); // should be stdout to reach sprig_audio }); var cwpitch = 650.0; socket.on('message77', function(mx) { var newfreq = freqHz - (16000/2048 * mx - cwpitch); setfreq(newfreq); }); socket.on('your message', function(msg) { serial.write(bufa); serial.write(msg ); serial.write(bufz); io.emit('your message', msg); }); }); // -- request freq -- function sendTime() { serial.write(buf3); } setInterval(sendTime, 100); // -- EOF --
index.html
<!doctype html> <html> <head> <style> * { margin: 0; padding: 0; box-sizing : border-box; } body { font: 13px Helvetica, Arial; } form { background: # 000; padding: 3px; position: fixed; bottom: 0; width: 100% ; } form input { border: 0; padding: 10px; width: 80% ; margin-right : .5% ; } form button { width: 15% ; background: rgb(143, 188, 143); border: none; padding: 10px; } #messages{list-style-type : none; margin : 0; padding : 0; } #messages li{padding : 5px 10px; } #messages li : nth-child(odd){background : #eee; } #messages {margin-bottom : 40px } #messages {font-size : 2em } #btn1 {position : fixed; top : 10px; left : 10px; font-size : 2em; background-color: darkseagreen } #btn2 {position : fixed; top : 10px; left : 60px; font-size : 2em; background-color: darkseagreen } #btn3 {position : fixed; top : 10px; left : 150px; font-size : 2em; background-color: cornsilk } #btn4 {position : fixed; top : 10px; left : 230px; font-size : 2em; background-color: cornsilk } #btn5 {position : fixed; top : 10px; left : 310px; font-size : 2em; background-color: paleturquoise } #btn6 {position : fixed; top : 10px; left : 390px; font-size : 2em; background-color: paleturquoise } #btn7 {position : fixed; top : 10px; left : 470px; font-size : 2em; background-color: thistle } #btn8 {position : fixed; top : 10px; left : 550px; font-size : 2em; background-color: thistle } #mycanvas0 {position : fixed; top : 70px; left : 10px; font-size : 2em } #mycanvas1 {position : fixed; top : 90px; left : 10px; font-size : 2em } #mycanvas2 {position : fixed; top : 620px; left : 10px; font-size : 2em } </style> </head> <body> <script src="https://code.jquery.com/jquery-1.11.1.js"></script> <script src='/socket.io/socket.io.js'></script> <script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script> <button id="btn1"><font size="5">CQ</font></button> <button id="btn2"><font size="5">QRZ?</font></button> <button id="btn3"><font size="5">VFO+</font></button> <button id="btn4"><font size="5">VFO-</font></button> <button id="btn5"><font size="5">BFO+</font></button> <button id="btn6"><font size="5">BFO-</font></button> <button id="btn7"><font size="5">BFO+</font></button> <button id="btn8"><font size="5">BFO-</font></button> <div id="messages"></div> <form action=""> <input id="m" autocomplete="off"/><button>Send</button> </form> <canvas id="mycanvas0" width="512" height= "20" style="background:white;"></canvas> <canvas id="mycanvas1" width="512" height="512" style="background:white;"></canvas> <canvas id="mycanvas2" width="280" height= "60" style="background:cornsilk;"></canvas> <script> function colormap(charcode) { // 0x00~0xff var tmp = charcode / 255.0; // 0.0~1.0 var val; var r, g, b; if (tmp < 0.50) { r = 0.0; } else if (tmp > 0.75) { r = 1.0; } else { r = 4.0 * tmp - 2.0; } if (tmp < 0.25) { g = 4.0 * tmp; } else if (tmp > 0.75) { g = -4.0 * tmp + 4.0; } else { g = 1.0; } if (tmp < 0.25) { b = 1.0; } else if (tmp > 0.50) { b = 0.0; } else { b = -4.0 * tmp + 2.0; } rgb[1] = 255.0 * g; rgb[0] = 255.0 * r; rgb[2] = 255.0 * b; } function waterFall(myarray) { ctx1.putImageData(imgData1, 0, 1); imgData1 = ctx1.getImageData(0, 0, 512, 512); for (j = 0; j < 512; j++) { colormap(myarray[j]); imgData1.data[0 + j * 4] = rgb[0]; imgData1.data[1 + j * 4] = rgb[1]; imgData1.data[2 + j * 4] = rgb[2]; imgData1.data[3 + j * 4] = 255; } } function showBpf(ctx0, imgData0, bfo) { var cwmarker0 = Math.floor( bfo[0] / (16000 / 2048) ); var cwmarker1 = Math.floor( bfo[1] / (16000 / 2048) ); console.log('bfo = ', bfo); for (i = 0; i < 20; i++) { for (j = 0; j < 512; j++) { var r, g, b, a; if(j == cwmarker0 || j == cwmarker1) { r = 0; g = 0; b = 0; } else if ( j >= cwmarker0-20 && j <= cwmarker0+20 && i > 10) { r = 135; g = 206; b= 235; } else if ( j >= cwmarker1-20 && j <= cwmarker1+20 && i > 10) { r = 216; g = 191; b= 216; } else { r = 255; g = 255; b= 255; } imgData0.data[0 + j * 4 + i * imgData0.width * 4] = r; imgData0.data[1 + j * 4 + i * imgData0.width * 4] = g; imgData0.data[2 + j * 4 + i * imgData0.width * 4] = b; imgData0.data[3 + j * 4 + i * imgData0.width * 4] = 255; } } ctx0.putImageData(imgData0, 0, 0); } function onClick(e) { var rect = e.target.getBoundingClientRect(); var mx = e.clientX - rect.left; var my = e.clientY - rect.top; console.log('mouse clicked: ', mx, my); socket.emit('message77', mx); } function playAudioStream(ctx, audio_f32) { var audio_buf = ctx.createBuffer(1, audio_f32.length, 16000); var audio_src = ctx.createBufferSource(); var current_time = ctx.currentTime; audio_buf.getChannelData(0).set(audio_f32); audio_src.buffer = audio_buf; audio_src.connect(ctx.destination); if (current_time < scheduled_time) { audio_src.start(scheduled_time); scheduled_time += audio_buf.duration; } else { audio_src.start(current_time); scheduled_time = current_time + audio_buf.duration; } } // main var socket = io(); var myarray = new Array(); var canvas0, canvas1, canvas2; var ctx0, ctx1; var imgData0, imgData1; var rgb = new Array(3); canvas0 = document.getElementById('mycanvas0'); canvas1 = document.getElementById('mycanvas1'); ctx0 = canvas0.getContext('2d'); ctx1 = canvas1.getContext('2d'); canvas1.addEventListener('click', onClick, false); imgData0 = ctx0.createImageData(512, 20); imgData1 = ctx1.createImageData(512, 512); var stage = new createjs.Stage("mycanvas2"); var t = new createjs.Text("IC-7410", "26px serif", "DarkRed"); t.x = 10; t.y = 10; stage.addChild(t); stage.update(); var bfo = [1000.0, 2000.0]; showBpf(ctx0, imgData0, bfo); for (i = 0; i < 512; i++) { for (j = 0; j < 512; j++) { imgData1.data[0 + j * 4 + i * imgData1.width * 4] = j % 256; imgData1.data[1 + j * 4 + i * imgData1.width * 4] = i % 256; imgData1.data[2 + j * 4 + i * imgData1.width * 4] = 128; imgData1.data[3 + j * 4 + i * imgData1.width * 4] = 255; } } ctx1.putImageData(imgData1, 0, 0); var audioCtx = new AudioContext; var scheduled_time = 0; socket.on('your message', function(msg) { $('#messages').append($('<div>').text(msg)); window.scrollTo(0, document.body.scrollHeight); }); socket.on('waterfall', function(data) { waterFall(data); }); socket.on('sound' , function(data) { playAudioStream(audioCtx, new Float32Array(data)); }); socket.on('freqmsg', function(msg) { t.text = msg; stage.update(); }); $('#btn1').click(function() { socket.emit('message1'); console.log('message1'); }); $('#btn2').click(function() { socket.emit('message2'); console.log('message2'); }); $('#btn3').click(function() { socket.emit('message3'); console.log('message3'); }); $('#btn4').click(function() { socket.emit('message4'); console.log('message4'); }); $('#btn5').click(function() { socket.emit('message5'); bfo[0]+=100; showBpf(ctx0, imgData0, bfo);}); $('#btn6').click(function() { socket.emit('message6'); bfo[0]-=100; showBpf(ctx0, imgData0, bfo);}); $('#btn7').click(function() { socket.emit('message7'); bfo[1]+=100; showBpf(ctx0, imgData0, bfo);}); $('#btn8').click(function() { socket.emit('message8'); bfo[1]-=100; showBpf(ctx0, imgData0, bfo);}); $('form').submit(function() { socket.emit('your message', $('#m').val()); $('#m').val(''); return false; }); </script> </body> </html>
sprig_audio.c
// file name = sprig_audio.c // (c) 2017 JH1OOD/Mike // % gcc -Wall -std=c99 sprig_audio.c -o sprig_audio -lm -lasound -lfftw3 #define NFFT 2048 #define NBFO 2 #define BAUDRATE B19200 #define M_PI 3.141592653589793 #include "asoundlib.h" #include <fftw3.h> #include <alloca.h> #include <complex.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> #include <math.h> #include <sched.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <termios.h> #include <unistd.h> static char myrig [256] = "/dev/ttyUSB0"; /* IC-7410 control */ static char device [256] = "hw:1,0"; /* capture device (IC-7410) */ static char device2[256] = "hw:0,0"; /* playback device (Raspberry) */ static unsigned int myrate [2] = { 16000, 16000}; /* in Hz */ static unsigned int mychannels [2] = { 1, 2}; /* number of channels */ static unsigned int mybuffer_time[2] = {512000, 512000}; /* in uS */ static unsigned int myperiod_time[2] = {128000, 128000}; /* in uS */ static snd_pcm_sframes_t mybuffer_size[2]; // 16kHz*512uS = 8192 frames static snd_pcm_sframes_t myperiod_size[2]; // 16kHz*128uS = 2048 frames (= NFFT) static int byte_per_sample = 2; /* 16 bit format */ int lring = 16384; static signed short *ringbuffer; // 0x4000 static int wpnt = 0; // 0x0000 static int rpnt = 16384 / 2; // 0x2000 static double complex ringbuffer2[151]; static double complex ringbuffer3[151]; static int wpnt2 = 0; static double audio_signal [NFFT]; static double fft_window [NFFT]; static int nbyte, nbyte2; static double bin_size; static double amax = 13.0, amin = 7.0; double bfo_freq [NBFO] = { 1000.0, 2000.0}; double bfo_phase[NBFO] = { 0.0, 0.0}; double bfo_delta[NBFO]; double cw_freq [NBFO] = { 400.0, 700.0}; double cw_phase [NBFO] = { 0.0, 0.0}; double cw_delta [NBFO]; const int nhilbert = 51; const double normalization = 1.0028050845; double hilbert_coeff[51] = { -1.244274239685367e-02 , // 0 +8.812508503481302e-16 , // 1 -9.007745694645722e-03 , // 2 -4.892921491956048e-16 , // 3 -1.224827000343725e-02 , // 4 -2.457355030119336e-16 , // 5 -1.626770260654829e-02 , // 5 -1.076580799233997e-15 , // 7 -2.126259131785366e-02 , // 8 +1.551646837953304e-15 , // 9 -2.754013546165147e-02 , // 10 -8.271548913440781e-16 , // 11 -3.555144114893793e-02 , // 12 +1.538194937222543e-16 , // 13 -4.616069183085086e-02 , // 14 -2.517547480270933e-16 , // 15 -6.087912031697079e-02 , // 16 +1.529522013663762e-16 , // 17 -8.310832605502005e-02 , // 18 -7.153673299041007e-16 , // 19 -1.216347994939241e-01 , // 20 -3.571474290448791e-17 , // 21 -2.087518418318809e-01 , // 22 -3.409056833722323e-16 , // 23 -6.354638176591967e-01 }; // 24 int ntap = 151; double filter_coeff2[151] = { -3.395305452627101e-04 , -3.451719723840665e-04 , -3.530612137292319e-04 , -3.631649177352709e-04 , -3.753639928353780e-04 , -3.894505086974910e-04 , -4.051255048204917e-04 , -4.219977489075693e-04 , -4.395834787202738e-04 , -4.573071519606507e-04 , -4.745032192165208e-04 , -4.904189252244857e-04 , -5.042181337480122e-04 , -5.149861613279066e-04 , -5.217355951351015e-04 , -5.234130602372101e-04 , -5.189068918766871e-04 , -5.070556589445432e-04 , -4.866574758121344e-04 , -4.564800311443923e-04 , -4.152712543468728e-04 , -3.617705329774267e-04 , -2.947203878567924e-04 , -2.128785068104263e-04 , -1.150300330287973e-04 , -6.347328586093010e-19 , 1.333341981065508e-04 , 2.860304138055082e-04 , 4.590695456642169e-04 , 6.533436439384927e-04 , 8.696444356953537e-04 , 1.108652380544769e-03 , 1.370926364961174e-03 , 1.656894139010764e-03 , 1.966843594141855e-03 , 2.300914974582423e-03 , 2.659094107869474e-03 , 3.041206732166469e-03 , 3.446913989373043e-03 , 3.875709143669074e-03 , 4.326915575142740e-03 , 4.799686087616610e-03 , 5.293003558798781e-03 , 5.805682949544904e-03 , 6.336374677422204e-03 , 6.883569348021978e-03 , 7.445603825678312e-03 , 8.020668613524446e-03 , 8.606816501260849e-03 , 9.201972427726550e-03 , 9.803944494460752e-03 , 1.041043605601629e-02 , 1.101905880293651e-02 , 1.162734674412459e-02 , 1.223277098690524e-02 , 1.283275520548310e-02 , 1.342469168181124e-02 , 1.400595779716308e-02 , 1.457393284800538e-02 , 1.512601505614670e-02 , 1.565963864062149e-02 , 1.617229081739082e-02 , 1.666152859271505e-02 , 1.712499521698524e-02 , 1.756043616788736e-02 , 1.796571453499896e-02 , 1.833882568225810e-02 , 1.867791107016340e-02 , 1.898127112601672e-02 , 1.924737705795075e-02 , 1.947488151683051e-02 , 1.966262801930648e-02 , 1.980965905524953e-02 , 1.991522281342583e-02 , 1.997877847048119e-02 , 2.000000000000000e-02 , 1.997877847048119e-02 , 1.991522281342583e-02 , 1.980965905524953e-02 , 1.966262801930648e-02 , 1.947488151683051e-02 , 1.924737705795075e-02 , 1.898127112601672e-02 , 1.867791107016340e-02 , 1.833882568225810e-02 , 1.796571453499896e-02 , 1.756043616788736e-02 , 1.712499521698524e-02 , 1.666152859271505e-02 , 1.617229081739082e-02 , 1.565963864062149e-02 , 1.512601505614670e-02 , 1.457393284800538e-02 , 1.400595779716308e-02 , 1.342469168181124e-02 , 1.283275520548310e-02 , 1.223277098690524e-02 , 1.162734674412459e-02 , 1.101905880293651e-02 , 1.041043605601629e-02 , 9.803944494460752e-03 , 9.201972427726550e-03 , 8.606816501260849e-03 , 8.020668613524446e-03 , 7.445603825678312e-03 , 6.883569348021978e-03 , 6.336374677422204e-03 , 5.805682949544904e-03 , 5.293003558798781e-03 , 4.799686087616610e-03 , 4.326915575142740e-03 , 3.875709143669074e-03 , 3.446913989373043e-03 , 3.041206732166469e-03 , 2.659094107869474e-03 , 2.300914974582423e-03 , 1.966843594141855e-03 , 1.656894139010764e-03 , 1.370926364961174e-03 , 1.108652380544769e-03 , 8.696444356953537e-04 , 6.533436439384927e-04 , 4.590695456642169e-04 , 2.860304138055082e-04 , 1.333341981065508e-04 , -6.347328586093010e-19 , -1.150300330287973e-04 , -2.128785068104263e-04 , -2.947203878567924e-04 , -3.617705329774267e-04 , -4.152712543468728e-04 , -4.564800311443923e-04 , -4.866574758121344e-04 , -5.070556589445432e-04 , -5.189068918766871e-04 , -5.234130602372101e-04 , -5.217355951351015e-04 , -5.149861613279066e-04 , -5.042181337480122e-04 , -4.904189252244857e-04 , -4.745032192165208e-04 , -4.573071519606507e-04 , -4.395834787202738e-04 , -4.219977489075693e-04 , -4.051255048204917e-04 , -3.894505086974910e-04 , -3.753639928353780e-04 , -3.631649177352709e-04 , -3.530612137292319e-04 , -3.451719723840665e-04 , -3.395305452627101e-04 }; int fd = -1; double *in; fftw_complex *out; fftw_plan p; static int myset_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, int id) { snd_pcm_uframes_t size; int err, dir; fprintf(stderr, "myset_hwparams: device id = %2d \n", id); 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; } err = snd_pcm_hw_params_set_rate_resample(handle, params, 0); // 0: no resampling if (err < 0) { fprintf(stderr, "Resampling setup failed for playback: %s\n", snd_strerror(err)); return err; } 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; } 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; } err = snd_pcm_hw_params_set_channels(handle, params, mychannels[id]); if (err < 0) { fprintf(stderr, "Channels count (%i) not available for the device: %s\n", mychannels[id], snd_strerror(err)); return err; } unsigned int rrate; rrate = myrate[id]; 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", myrate[id], snd_strerror(err)); return err; } if (rrate != myrate[id]) { fprintf(stderr, "set_hwparams2: Rate doesn't match (requested %iHz, get %iHz)\n", myrate[id], err); return -EINVAL; } err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &mybuffer_time[id], &dir); if (err < 0) { fprintf(stderr, "Unable to set buffer time %i for the device: %s\n", mybuffer_time[id], 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; } mybuffer_size[id] = size; err = snd_pcm_hw_params_set_period_time_near(handle, params, &myperiod_time[id], &dir); if (err < 0) { fprintf(stderr, "Unable to set period time %i for playback: %s\n", myperiod_time[id], 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; } myperiod_size[id] = size; 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; } fprintf(stderr, " obtained rate = %8d \n", rrate); fprintf(stderr, " mybuffer_size = %8ld \n", mybuffer_size[id]); fprintf(stderr, " myperiod_size = %8ld \n", myperiod_size[id]); return 0; } // --------------------------------------------------------------------- static int myset_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int id) { int err; fprintf(stderr, "myset_swparams: device id = %2d \n", id); 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; } err = snd_pcm_sw_params_set_start_threshold( handle, swparams, (mybuffer_size[id] / myperiod_size[id]) * myperiod_size[id]); if (err < 0) { fprintf(stderr, "Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); return err; } err = snd_pcm_sw_params_set_avail_min(handle, swparams, myperiod_size[id]); if (err < 0) { fprintf(stderr, "Unable to set avail min for playback: %s\n", snd_strerror(err)); return err; } 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_callback2(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; static int icount = 0; signed char mychar[4096]; avail = snd_pcm_avail_update(handle); while (avail >= myperiod_size[1]) { err = snd_pcm_writei(handle, samples, myperiod_size[1]); if (err < 0) { fprintf(stderr, "Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if (err != myperiod_size[1]) { fprintf(stderr, "Write error: written %i expected %li\n", err, myperiod_size[1]); exit(EXIT_FAILURE); } if(0) fprintf( stderr, "async_callback2: icount = %8d, avail = %12ld, period_size = %12ld \n", icount++, avail, myperiod_size[1]); for (int i = 0; i < myperiod_size[1]; i++) { // also NFFT audio_signal[i] = ringbuffer[rpnt]; double val2 = 0.0; // hilbert filter output int index = 0; for (int j=0;j<nhilbert;j++) { index = rpnt - j; if (index < 0) { index += 16384; } val2 += ringbuffer[index] * hilbert_coeff[j]; } int idelayed = rpnt - (nhilbert-1)/2; if (idelayed < 0) idelayed += 16834; ringbuffer2[wpnt2] = (ringbuffer[idelayed] + I * val2) * (cos(bfo_phase[0]) - I * sin(bfo_phase[0])); ringbuffer3[wpnt2] = (ringbuffer[idelayed] + I * val2) * (cos(bfo_phase[1]) - I * sin(bfo_phase[1])); double complex val3 = 0.0; double complex val4 = 0.0; int index3 = 0; for(int j=0;j<ntap;j++) { index3 = wpnt2 - j; if(index3 < 0) index3 += ntap; val3 += ringbuffer2[index3] * filter_coeff2[j]; val4 += ringbuffer3[index3] * filter_coeff2[j]; } val3 *= cos(cw_phase [0]) + I * sin(cw_phase [0]); val4 *= cos(cw_phase [1]) + I * sin(cw_phase [1]); samples[2 * i] = (signed short) creal(val3); samples[2 * i + 1] = (signed short) creal(val4); mychar [i] = ( (signed short) creal(val3 + val4) ) >> 8; rpnt++; rpnt &= 0x3fff; wpnt2++; if(wpnt2 == ntap) wpnt2 = 0; for(int j=0;j<NBFO;j++) { bfo_phase[j] += bfo_delta[j]; cw_phase [j] += cw_delta [j]; } } for (int i = 0; i < NFFT; i++) { in[i] = fft_window[i] * audio_signal[i]; } fftw_execute(p); for (int i = 0; i < NFFT / 4; i++) { // NFFT/4 = 2048/4 = 512 double val; val = out[i][0] * out[i][0] + out[i][1] * out[i][1]; if (val < pow(10.0, amin)) { val = 0.0; } else if (val > pow(10.0, amax)) { val = 1.0; } else { val = (log10(val) - amin) / (amax - amin); } unsigned char c = 255 * val; if(i<512) fprintf(stdout, "%c", c); // apple } for (int i= 0;i<2048;i++) { fprintf(stdout, "%c", mychar[i]); // apple } fflush(stdout); avail = snd_pcm_avail_update(handle); } // end of while } // --------------------------------------------------------------------- static int async_loop2(snd_pcm_t *handle, signed short *samples) { snd_async_handler_t *ahandler; int err, count; err = snd_async_add_pcm_handler(&ahandler, handle, async_callback2, samples); if (err < 0) { fprintf(stderr, "Unable to register async handler\n"); exit(EXIT_FAILURE); } for (count = 0; count < 2; count++) { err = snd_pcm_writei(handle, samples, myperiod_size[1]); if (err < 0) { fprintf(stderr, "Initial write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if (err != myperiod_size[1]) { fprintf(stderr, "Initial write error: written %i expected %li\n", err, myperiod_size[1]); 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); } } 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; static int icount = 0; static double phase = 0.0; double phase_delta; static double amp = 0.0; phase_delta = 2.0 * M_PI * 2000.0 / 16000.0; phase_delta *= 1.0 + 0.1 * sin(2.0 * M_PI * icount / 180.0); avail = snd_pcm_avail_update(handle); while (avail >= myperiod_size[0]) { err = snd_pcm_readi(handle, samples, myperiod_size[0]); if (err < 0) { fprintf(stderr, "Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if (err != myperiod_size[0]) { fprintf(stderr, "Write error: written %i expected %li\n", err, myperiod_size[0]); exit(EXIT_FAILURE); } int up, down; if( icount % 4 == 0) { up = 1; down = 0; } else if (icount % 4 == 3) { up = 0; down = 1; } else { up = 0; down = 0; } if(0) fprintf( stderr, "async_callback : icount = %8d, avail = %12ld, amp = %10.4f up = %3d, down = %3d \n", icount, avail, amp, up, down); for (int i = 0; i < NFFT; i++) { if(up == 1) { if(i < 20) { amp = 1.0-cos(M_PI*i/20.0); } else { amp = 2.0; } } if (down == 1) { if(i < 20) { amp = 1.0+cos(M_PI*i/20.0); } else { amp = 0.0; } } ringbuffer[wpnt++] = 1.0 * samples[i] + 0.0 * amp *sin (phase); phase += phase_delta; wpnt &= 0x3fff; } avail = snd_pcm_avail_update(handle); } icount++; } // --------------------------------------------------------------------- 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); } } return 0; } // --------------------------------------------------------------------- void serial_init(void) { struct termios tio; memset(&tio, 0, sizeof(tio)); tio.c_cflag = CS8 | CLOCAL | CREAD; tio.c_cc[VEOL] = 0xfd; /* IC-7410 postamble */ tio.c_lflag = 0; /* non canonical mode */ tio.c_cc[VTIME] = 0; /* non canonical mode */ tio.c_cc[VMIN] = 1; /* non canonical mode */ tio.c_iflag = IGNPAR | ICRNL; cfsetispeed(&tio, BAUDRATE); cfsetospeed(&tio, BAUDRATE); tcsetattr(fd, TCSANOW, &tio); } // --------------------------------------------------------------------- int main(int argc, char *argv[]) { struct termios oldtio; snd_pcm_t *handle; snd_pcm_t *handle2; snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_t *hwparams2; snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_t *swparams2; signed short *samples; signed short *samples2; int err; int id; /* 0: capture device, 1: playback device */ char buf4stdin[1024]; for(int i=0;i<NBFO;i++) { bfo_delta[i] = 2.0 * M_PI * bfo_freq[i] / myrate[1]; cw_delta [i] = 2.0 * M_PI * cw_freq [i] / myrate[1]; } setvbuf(stdout, NULL, _IOFBF, 0); if (argc != 4) { fprintf(stderr, "Usage %s /dev/ttyUSB0 hw:1,0 hw:0,0 \n", argv[0]); fprintf(stderr, " try ls -l /dev/ttyUSB*; arecord -l; aplay -l to know " "these parameters.\n"); return -1; } strcpy(myrig , argv[1]); strcpy(device , argv[2]); strcpy(device2, argv[3]); fprintf(stderr, "serial device is [%s] \n", myrig); fprintf(stderr, "audio capture device is [%s] \n", device); fprintf(stderr, "audio output device is [%s] \n", device2); bin_size = myrate[0] / (double)NFFT; fprintf(stderr, "NFFT = %d, bin_size = %f [Hz] \n", NFFT, bin_size); for (int i = 0; i < NFFT; i++) { fft_window[i] = 0.54 - 0.46 * cos(2.0 * M_PI * i / (double)NFFT); } in = malloc(sizeof(double) * NFFT); out = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * (NFFT / 2 + 1)); p = fftw_plan_dft_r2c_1d(NFFT, in, out, FFTW_MEASURE); snd_pcm_hw_params_alloca(&hwparams); snd_pcm_hw_params_alloca(&hwparams2); snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_alloca(&swparams2); if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) { fprintf(stderr, "Audio capture device open error: %s\n", snd_strerror(err)); return 0; } if ((err = snd_pcm_open(&handle2, device2, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { fprintf(stderr, "Audio output device open error: %s\n", snd_strerror(err)); return 0; } id = 0; /* capture device */ if ((err = myset_hwparams(handle, hwparams, id)) < 0) { fprintf(stderr, "Setting of hwparams failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if ((err = myset_swparams(handle, swparams, id)) < 0) { fprintf(stderr, "Setting of swparams failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } id = 1; /* playback device */ if ((err = myset_hwparams(handle2, hwparams2, id)) < 0) { fprintf(stderr, "Setting of hwparams failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if ((err = myset_swparams(handle2, swparams2, id)) < 0) { fprintf(stderr, "Setting of swparams failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } nbyte = myperiod_size[0] * mychannels[0] * byte_per_sample; nbyte2 = myperiod_size[1] * mychannels[1] * byte_per_sample; samples = malloc(nbyte); if (samples == NULL) { fprintf(stderr, "cannot malloc samples \n"); exit(EXIT_FAILURE); } samples2 = malloc(nbyte2); if (samples2 == NULL) { fprintf(stderr, "cannot malloc samples2 \n"); exit(EXIT_FAILURE); } ringbuffer = malloc( sizeof(short) * lring); if (ringbuffer == NULL) { fprintf(stderr, "cannot malloc ringbuffer \n"); exit(EXIT_FAILURE); } for (int i = 0; i < lring; i++) { ringbuffer[i] = 8096.0 * sin(i * 2.0 * 3.14 / 32.0); } for (int i = 0; i < nbyte2 / byte_per_sample; i++) { samples2[2 * i + 0] = 8096.0 * sin(i * 2.0 * 3.14 / 64.0); samples2[2 * i + 1] = 8096.0 * sin(i * 2.0 * 3.14 / 64.0); } hilbert_coeff[(nhilbert-1)/2] = 0.0; // 25 for (int i=((nhilbert-1)/2)+1;i<nhilbert;i++) { hilbert_coeff[i] = - hilbert_coeff[(nhilbert-1)-i]; // 26 <- -24 } if ((err = async_loop(handle, samples)) < 0) { fprintf(stderr, "async_loop set error: %s\n", snd_strerror(err)); } if ((err = async_loop2(handle2, samples2)) < 0) { fprintf(stderr, "async_loop2 set error: %s\n", snd_strerror(err)); } 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(); while (1) { fgets(buf4stdin,512,stdin); if(buf4stdin[0] == '0') { bfo_freq [0] += 100.0; bfo_delta[0] = 2.0 * M_PI * bfo_freq [0] / myrate[1]; fprintf(stderr, "bfo_freq = %f \n", bfo_freq[0]); } if(buf4stdin[0] == '1') { bfo_freq [0] -= 100.0; bfo_delta[0] = 2.0 * M_PI * bfo_freq [0] / myrate[1]; fprintf(stderr, "bfo_freq = %f \n", bfo_freq[0]); } if(buf4stdin[0] == '2') { bfo_freq [1] += 100.0; bfo_delta[1] = 2.0 * M_PI * bfo_freq [1] / myrate[1]; fprintf(stderr, "bfo_freq = %f \n", bfo_freq[1]); } if(buf4stdin[0] == '3') { bfo_freq [1] -= 100.0; bfo_delta[1] = 2.0 * M_PI * bfo_freq[1] / myrate[1]; fprintf(stderr, "bfo_freq = %f \n", bfo_freq[1]); } sleep(1); } fftw_destroy_plan(p); fftw_free(in); fftw_free(out); return 0; }