Analytic Signals (2)

The source code is just for a test.

// test.c
#include <stdio.h>
#include <math.h>
#include <complex.h>
#include <stdlib.h>
#define  PI 3.1415926535

int main () {
	const int nhilbert = 51;
	const double normalization = 1.0028050845;
	double hilbert[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

	double wave0[1024], wave1[1024], wave2[1024];
	double complex cwave1[1024], cwave2[1024], cbfo[1024];
	double fsample = 32000.0;
	double fsignal =  1000.0;
	double fbfo    =  1010.0;
	double amp     = 1.0;
	double phase   = (45.0/360.0)*2.0*PI;

	hilbert[(nhilbert-1)/2] = 0.0;		// 25
	for (int i=((nhilbert-1)/2)+1;i<nhilbert;i++) {
		hilbert[i] = - hilbert[(nhilbert-1)-i];	// 26 <- -24
	}
	for (int i=0;i<nhilbert;i++) {
		printf("a+ %20.10f \n", hilbert[i]);
	}

	for (int i=0;i<1024;i++) {
		if( (i>=100) && (i<150) ) {
			amp = (i-100)/50.0;
		} else if ( (i>=120) && (i<800) ) {
			amp = 1.0;
		} else if ( (i>=800) && (i<850) ) {
			amp = 1.0 - (i-800)/50.0;
		} else {
			amp = 0.0;
		}
		cbfo [i] = cos(i*2.0*PI/(fsample/fbfo)+phase)
	             - I * sin(i*2.0*PI/(fsample/fbfo)+phase);
		wave0[i] = amp * sin(i*2.0*PI/(fsample/fsignal));
		wave1[i] = 0.0;
		wave2[i] = 0.0;
		printf("b+ %20.10f \n", wave0[i]);
	}

	for (int i=nhilbert;i<1024-nhilbert;i++) {
		wave1[i] = wave0[i-((nhilbert-1)/2)];
		printf("c+ %20.10f \n", wave1[i]);
	}

	for (int i=nhilbert;i<1024-nhilbert;i++) {
		for(int j=0;j<nhilbert;j++) {
			wave2[i] += wave0[i-j] * hilbert[j];
		}
		wave2[i] /= normalization;
		printf("d+ %20.10f \n", wave2[i]);
	}

	for (int i=0;i<1024;i++) {
		cwave1[i] = wave1[i] + I * wave2[i];
	}

	for (int i=0;i<1024;i++) {
		cwave2[i] = cwave1[i] * cbfo[i];
		printf("e+ %20.10f \n", creal(cwave2[i]));
	}
		
	for (int i=0;i<1024;i++) {
		printf("f+ %20.10f \n", cimag(cwave2[i]));
	}
		
	for (int i=0;i<1024;i++) {
		printf("g+ %20.10f \n", cabs(cwave2[i]));
	}
		
	return 0;
}

bash.sh

  grep a+ ttt | colrm 1 2 > Hilbert
  grep b+ ttt | colrm 1 2 > Waveform
  grep c+ ttt | colrm 1 2 > Re
  grep d+ ttt | colrm 1 2 > Im
  grep e+ ttt | colrm 1 2 > Sig-Re
  grep f+ ttt | colrm 1 2 > Sig-Im
  grep g+ ttt | colrm 1 2 > Sig-Abs
% gcc test.c -o test -lm && ./test > ttt && ./bash.sh
% gnuplot
gnuplot> plot "Re" w lp, "Sig-Re" w lp, "Sig-Im" w lp, "Sig-abs" w lp

Analytic Signals

You can generate an analytic signal from a real-valued signal, say, from your conventional receivers without an I/Q output, by using a Hilbert filter.

Once you have an analytic signal, frequency conversion becomes a very straightforward process. In the figure, “Re” is the incoming real-valued signal around 10kHz, “Im” is the imaginary part of the obtained analytic signal, and “Sig” is the down converted version of the incoming signal now at 700Hz.

You can also down convert to (almost) DC, but in that case you need to observe both real and imaginary parts to compute the power of the signal unless you control the phase of your BFO.

Under the presence of some sinusoidal and random noise, we do not get a very clean result and need to do some filtering.

IC-7410 Rig Control with http (8)

You can click on a trace on the waterfall image to tune into that frequency.

index.html

var cwmarker1 = Math.floor(  cwpitch        / (32000 / 4096) );
var cwmarker2 = Math.floor( (cwpitch - 100) / (32000 / 4096) );
var cwmarker3 = Math.floor( (cwpitch + 100) / (32000 / 4096) );

function onClick(e) {
  var rect = e.target.getBoundingClientRect();
  var mx   = e.clientX - rect.left;
  var my   = e.clientY - rect.top;
  socket.emit('message7', mx);
}
canvas1.addEventListener('click', onClick, false);

index.js

  socket.on('message7', function(mx) {
    var newfreq = freqHz - (32000/4096 * mx - cwpitch);
    setfreq(newfreq);
  });

Of course, you must consider which sideband you are listening to in calculating your new frequency.

IC-7410 Rig Control with http (7)

You can change the VFO frequency of your remote IC-7410 by clicking the buttons on the browser.

index.js

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 = 7026000;

var ndata   = 512;  // data[512] + 0x0a
var pos     = 0;
var myarray = new Array();

// -- water fall --

var count = 0;
process.stdin.on('readable', function() {
  var buf = process.stdin.read();
  if (buf !== null) {
    for (var i = 0; i < buf.length; i++) {
      if (buf[i] == 0x0a) {
        if (pos >= 512) {
          io.emit('waterfall', myarray);
        }
        pos = 0;
      } else {
        myarray[pos++] = buf[i];
      }
    }
  }
});

// -- serial for IC-7410 --

serial.on('open',
          function() { console.log('serial port /dev/ttyUSB0 is opened.'); });

var count2 = 0;
serial.on('data', function(data) {
  //    console.log('received data: ', data.length, data);
  if (!(data[0] == 0xfe & data[1] == 0xfe)) {
    console.log('------------- received serial data 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;
    //        console.log('count2 = ', count2++, 'freq: ' + freq, freqHz);
    io.emit('freqmsg', 'VFO A: ' + freq);
  }

});

serial
    .on('error', function(err) { console.log('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);
    }

// -- socket --

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 + 1000;
    setfreq(newfreq);
  });

  socket.on('message4', function() {
    var newfreq = freqHz + 100;
    setfreq(newfreq);
  });

  socket.on('message5', function() {
    var newfreq = freqHz - 100;
    setfreq(newfreq);
  });

  socket.on('message6', function() {
    var newfreq = freqHz - 1000;
    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);
  // console.log('Freq asked..');
}

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(130, 224, 255); 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 }
#btn2 {position : fixed; top : 10px; left :  70px; font - size : 2em }
#btn3 {position : fixed; top : 10px; left : 150px; font - size : 2em; width : 30px }
#btn4 {position : fixed; top : 10px; left : 190px; font - size : 2em; width : 30px }
#btn5 {position : fixed; top : 10px; left : 230px; font - size : 2em; width : 30px }
#btn6 {position : fixed; top : 10px; left : 270px; font - size : 2em; width : 30px }
#frequency {position : fixed; top :  10px; left : 310px; font - size : 2em }
#mycanvas  {position : fixed; top :  60px; left :  10px; font - size : 2em }
#mycanvas2 {position : fixed; top : 600px; left :  10px; font - size : 2em }
</style>

<script>
  window.addEventListener("load", init);
  function init(){}
</script>
</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">++</ font></button>
    <button id="btn4"><font size="5">+</font></button>
    <button id="btn5"><font size="5">-</font></button>
    <button id="btn6"><font size="5">--</font></button>

    <div id="frequency"></div>
    <div id="messages"></div>
    <form action="">
      <input id="m" autocomplete="off"/><button>Send</button>
    </form>
    <canvas id="mycanvas"  width="512" height="512" style="background:white;"></canvas>
    <canvas id="mycanvas2" width="512" height= "90" style="background:white;"></canvas>

<script>
var myarray = new Array();
var socket  = io();
var canvas;
var ctx;
var imgData;
var rgb = new Array(3);

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

function colormap(charcode) {         // 0~9 or 0x30~0x39
  var tmp = (charcode - 0x30) * 0.1;  // 0.0~0.9
  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) {
  ctx.putImageData(imgData, 0, 1);
  imgData = ctx.getImageData(0, 0, 512, 512);
  for (j = 0; j < 512; j++) {
    colormap(myarray[j]);
    imgData.data[0 + j * 4] = rgb[0];
    imgData.data[1 + j * 4] = rgb[1];
    imgData.data[2 + j * 4] = rgb[2];
    imgData.data[3 + j * 4] = 255;
  }
}

canvas = document.getElementById('mycanvas');
ctx    = canvas.getContext('2d');

var mx, my;
function onClick(e) {
  var rect = e.target.getBoundingClientRect();
  mx       = e.clientX - rect.left;
  my       = e.clientY - rect.top;
  console.log('mouse clicked: ', mx, my);
}
canvas.addEventListener('click', onClick, false);

imgData = ctx.createImageData(512, 512);
for (i = 0; i < 512; i++) {
  for (j = 0; j < 512; j++) {
    imgData.data[0 + j * 4 + i * imgData.width * 4] = j % 256;
    imgData.data[1 + j * 4 + i * imgData.width * 4] = i % 256;
    imgData.data[2 + j * 4 + i * imgData.width * 4] = 128;
    imgData.data[3 + j * 4 + i * imgData.width * 4] = 255;
  }
}
ctx.putImageData(imgData, 0, 0);

var socket = io();

$('#btn1').click(function() { socket.emit('message1'); });
$('#btn2').click(function() { socket.emit('message2'); });
$('#btn3').click(function() { socket.emit('message3'); });
$('#btn4').click(function() { socket.emit('message4'); });
$('#btn5').click(function() { socket.emit('message5'); });
$('#btn6').click(function() { socket.emit('message6'); });

$('form').submit(function() {
  socket.emit('your message', $('#m').val());
  $('#m').val('');
  return false;
});

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('freqmsg', function(msg) {
  t.text = msg;
  stage.update();
  document.getElementById("frequency").innerHTML = msg;
});

</script>

</body>
</html>

IC-7410 Rig Control with http (6)

This is a real waterfall.

By the way, if you get an error message, undefined reference to ‘alloca’, with your gcc, the following line may help.

#include <alloca.h>

If you are using “stdout” instead of “stderr”, you may need to specify the mode for file buffering.

% sprig_audio_only /dev/ttyUSB0 hw:2,0 |& node index.js
% sprig_audio_only /dev/ttyUSB0 hw:2,0 |  node index.js

sprig_audio_only.c

#include <stdio.h>
int main () {
 setvbuf(stdout, NULL, _IOLBF, 0); // line buffering mode
//
 for(int i=0;i<512<i++) {
  fprintf(stdout, "%1d", audio_signal_ffted[i]);
 }
 fprintf(stdout,"\n");
 fflush(stdout); // this will also do with full buffering mode
//
}

IC-7410 Rig Control with http (5)

If we put the different parts together, you will have something like this. Note that the waterfall data is a computer generated one.

index.js

// file name = index20.js, reads index20.html

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 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 + '/index20.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 ndata   = 512;  // data[512] + 0x0a
var pos     = 0;
var myarray = new Array();

// -- water fall --

process.stdin.on('readable', function() {
  var buf = process.stdin.read();
  if (buf !== null) {
    for (var i = 0; i < buf.length; i++) {
      if (buf[i] == 0x0a) {
        pos = 0;
        io.emit('waterfall', myarray);
      } else {
        myarray[pos++] = buf[i];
      }
    }
  }
});

// -- serial for IC-7410 --

serial.on('open',function(){
    console.log('serial port /dev/ttyUSB0 is opened.');
});

serial.on('data',function(data){
    console.log('received data: ', data.length, data);
    if ( !(data[0] == 0xfe & data[1] == 0xfe) ) {
      console.log('** received serial data 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";
        console.log('freq: ' + freq);
        io.emit('freqmsg', 'VFO A: ' + freq);
    }

});

serial.on('error', function(err) {
  console.log('Error: ', err.message);
})

// -- socket --

io.on('connection', function(socket){

    socket.on('message1', function(){
    serial.write(buf1);
  });

    socket.on('message2', function(){
    serial.write(buf2);
  });

  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 --

IC-7410 Rig Control with http (4)

Changed the color mapping and some parts of the code.

index.js

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 ndata   = 512;  // data[512] + 0x0a
var pos     = 0;
var myarray = new Array();

process.stdin.on('readable', function() {
  var buf = process.stdin.read();
  if (buf !== null) {
    for (var i = 0; i < buf.length; i++) {
      if (buf[i] == 0x0a) {
        pos = 0;
        io.emit('waterfall', myarray);
      } else {
        myarray[pos++] = buf[i];
      }
    }
  }
});

process.stdin.on('end', function() {
});

index.html

<!doctype html>
<html>
<head>
<script src='/socket.io/socket.io.js'></script>
<script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script>

<script>
var myarray = new Array();
var socket  = io();
var canvas;
var ctx;
var imgData;

window.addEventListener("load", init);

function init() {
  canvas = document.getElementById('myCanvas');
  ctx    = canvas.getContext('2d');

  imgData = ctx.createImageData(512, 512);
  for (i = 0; i < 512; i++) {
    for (j = 0; j < 512; j++) {
      imgData.data[0 + j * 4 + i * imgData.width * 4] = j % 256;
      imgData.data[1 + j * 4 + i * imgData.width * 4] = i % 256;
      imgData.data[2 + j * 4 + i * imgData.width * 4] = 128;
      imgData.data[3 + j * 4 + i * imgData.width * 4] = 255;
    }
  }
  ctx.putImageData(imgData, 0, 0);
}

function waterFall(myarray) {
  ctx.putImageData(imgData, 0, 1);
  imgData = ctx.getImageData(0, 0, 512, 512);
  for (j = 0; j < 512; j++) {
    var tmp                 = ((myarray[j] - 48) * 25 % 256) / 256.0;
    imgData.data[0 + j * 4] = colormap_r(tmp);
    imgData.data[1 + j * 4] = colormap_g(tmp);
    imgData.data[2 + j * 4] = colormap_b(tmp);
    imgData.data[3 + j * 4] = 255;
  }
}

socket.on('waterfall', function(data) { waterFall(data); });

function colormap_r(tmp) {
  var val;
  if (tmp < 0.50) {
    val = 0.0;
  } else if (tmp > 0.75) {
    val = 1.0;
  } else {
    val = 4.0 * tmp - 2.0;
  }
  return 255.0 * val;
}

function colormap_g(tmp) {
  var val;
  if (tmp < 0.25) {
    val = 4.0 * tmp;
  } else if (tmp > 0.75) {
    val = -4.0 * tmp + 4.0;
  } else {
    val = 1.0;
  }
  return 255.0 * val;
}

function colormap_b(tmp) {
  var val;
  if (tmp < 0.25) {
    val = 1.0;
  } else if (tmp > 0.50) {
    val = 0.0;
  } else {
    val = -4.0 * tmp + 2.0;
  }
  return 255.0 * val;
}

</script>
</head>

<body>
    <canvas id="myCanvas" width="512" height="512" style="background:white;"></canvas>
</body>
</html>

IC-7410 Rig Control with http (3)

The window on the right is with sprig, a C program running on a remote host to which IC-7410 is attached via an USB I/F.

The window on the left is a browser showing the same waterfall image.

You see four different browsers on a local host. They are Chrome, Firefox, Opera, and Safari. Can you tell which is which?

Remote host:

% sprig /dev/ttyUSB0 HW:2,0 |& node index.js

sprig.c

  for(int j=0;j<WATERFALL_XSIZE;j++) {
       double tmp  =  audio_signal_ffted[j];
       int    iii  = 10.0*tmp; fprintf(stderr, "%1d", iii);
       *p++ = *q++ = colormap_r(tmp);
       *p++ = *q++ = colormap_g(tmp);
       *p++ = *q++ = colormap_b(tmp);
  }

index.js

var http  = require('http');
var fs    = require('fs');
var index = fs.readFileSync(__dirname + '/index.html');
var count = 0;

var app = http.createServer(function(req, res) {
  res.writeHead(200, {'Content-Type' : 'text/html'});
  res.end(index);
});

var io = require('socket.io').listen(app);
app.listen(3000);

var ndata = 512;
var pos   = 0;
var myarray = new Array(ndata);

process.stdin.on('readable', function() {
  var buf = process.stdin.read();
  for (var i = 0; i < buf.length; i++) {
    myarray[pos++] = buf[i];
    if(pos == ndata) {
      pos = 0;
      console.log(myarray[0], myarray[1], myarray[2]);
      io.emit('time', {buffer : myarray});
    }
  }
});

index.html

<!doctype html>
<html>
<head>
<script src='/socket.io/socket.io.js'></script>
<script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script>
<script>
var myarray = Array();
var socket  = io();
var canvas;
var ctx;
var imgData;
function init() {
  canvas = document.getElementById('myCanvas');
  ctx    = canvas.getContext('2d');

    imgData = ctx.createImageData(512, 512);
    for (i = 0; i < 512; i++) {
      for (j = 0; j < 512; j++) {
        imgData.data[0 + j * 4 + i * imgData.width * 4] = j % 256;
        imgData.data[1 + j * 4 + i * imgData.width * 4] = i % 256;
        imgData.data[2 + j * 4 + i * imgData.width * 4] = 0;
        imgData.data[3 + j * 4 + i * imgData.width * 4] = 255;
      }
    }
    ctx.putImageData(imgData, 0, 0);
}

function emitParticles(myarray) {
  ctx.putImageData(imgData, 0, 1);
  imgData = ctx.getImageData(0, 0, 512, 512);
  for (j = 0; j < 512; j++) {
    var index = (myarray[j] - 48) * 32 % 256;
    imgData.data[0 + j * 4] = index;
    imgData.data[1 + j * 4] = 256 - index;
    imgData.data[2 + j * 4] = 0;
    imgData.data[3 + j * 4] = 255;
  }
}

socket.on('time', function(data) {
  myarray = data.buffer;
  emitParticles(myarray);
console.log(count);
});
</script>
</head>

<body>
    <canvas id="myCanvas" width="512" height="512" style="background:white;"></canvas>
    <p id='messages'></p>
</body>
</html>

Waterfall with createImageData (2)

By using getImageData and putImageData with an appropriate offset value, you do not have to explicitly shift the data in imgData.

index.html

<!doctype html>
<html>
<head>
<script src='/socket.io/socket.io.js'></script>
<script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script>
<script>
var nxdata  = 32;
var nydata  = 32;
var myarray = Array(nxdata);
var mytable = Array(nxdata * nydata);
var socket  = io();
var canvas;
var ctx;
var imgData;

window.addEventListener("load", init);

function init() {
  canvas = document.getElementById('myCanvas');
  ctx    = canvas.getContext('2d');
    imgData = ctx.createImageData(512, 512);
    for (i = 0; i < 512; i++) {
      for (j = 0; j < 512; j++) {
        imgData.data[0 + j * 4 + i * imgData.width * 4] = j % 256;
        imgData.data[1 + j * 4 + i * imgData.width * 4] = (255 - (i % 256));
        imgData.data[2 + j * 4 + i * imgData.width * 4] =
            ((i / 2 + j / 2) % 256);
        imgData.data[3 + j * 4 + i * imgData.width * 4] = 255;
      }
    }
    ctx.putImageData(imgData, 0, 0);
}

function emitParticles(myarray) {
  ctx.putImageData(imgData, 0, 1);
  imgData = ctx.getImageData(0, 0, 512, 512);
  for (j = 0; j < 512; j++) {
    imgData.data[0 + j * 4] = (myarray[(j>>4)%8] - 48) * 32 % 256;
    imgData.data[1 + j * 4] = j % 256;
    imgData.data[2 + j * 4] = 0;
    imgData.data[3 + j * 4] = 255;
  }
}

socket.on('time', function(data) {
  myarray = data.buffer;
  emitParticles(myarray);
});
</script>
</head>

<body>
    <canvas id="myCanvas" width="512" height="512" style="background:white;"></canvas>
    <p id='messages'></p>
</body>
</html>

Waterfall with createImageData

A more natural way to draw waterfall pictures is to use the methods, createImageData, getImageData, and putImageData.

index.html

<!doctype html>
<html>
<head>
<script src='/socket.io/socket.io.js'></script>
<script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script>
<script>
var nxdata  = 32;
var nydata  = 32;
var myarray = Array(nxdata);
var mytable = Array(nxdata * nydata);
var socket  = io();
var canvas;
var ctx;
var imgData;

window.addEventListener("load", init);

function init() {
  console.log('init..');
  canvas = document.getElementById('myCanvas');
  ctx    = canvas.getContext('2d');

  if (canvas.getContext && canvas.getContext('2d').createImageData) {
    imgData = ctx.createImageData(512, 512);
    for (i = 0; i < 512; i++) {
      for (j = 0; j < 512; j++) {
        imgData.data[0 + j * 4 + i * imgData.width * 4] = j % 256;
        imgData.data[1 + j * 4 + i * imgData.width * 4] = (255 - (i % 256));
        imgData.data[2 + j * 4 + i * imgData.width * 4] =
            ((i / 2 + j / 2) % 256);
        imgData.data[3 + j * 4 + i * imgData.width * 4] = 255;
      }
    }
    ctx.putImageData(imgData, 0, 0);
  }
}

var count  = 0;
var marker = 0;
function emitParticles(myarray) {
  count++;
  if (count % 128 == 0) {
    marker = 255;
    count  = 0;
  } else {
    marker = 0;
  }
  for (j = 0; j < 512; j++) {
    imgData.data[0 + j * 4 + (512 - 1) * imgData.width * 4] = marker;
    imgData.data[1 + j * 4 + (512 - 1) * imgData.width * 4] = 0;
    imgData.data[2 + j * 4 + (512 - 1) * imgData.width * 4] =
        (32 * myarray[(j >> 3) % 10]) % 256;
    imgData.data[3 + j * 4 + (512 - 1) * imgData.width * 4] = 255;
  }

  for (i = 0; i < 512 - 1; i++) {
    for (j = 0; j < 512; j++) {
      for (k = 0; k < 4; k++) {
        imgData.data[k + j * 4 + i * imgData.width * 4] =
            imgData.data[k + j * 4 + (i + 1) * imgData.width * 4];
      }
    }
  }

  ctx.putImageData(imgData, 0, 0);
}

socket.on('time', function(data) {
  myarray = data.buffer;
  emitParticles(myarray);
});
</script>
</head>

<body>
    <canvas id="myCanvas" width="512" height="512" style="background:white;">
    </canvas>
    <p id='messages'></p>
</body>
</html>