Realtime graphs

The example below plots the spectrum of a random generated 64-QAM modulation:

Example code

var options = {
	appendTo : "spectrum",
	canvasWidth : 800,
	canvasHeight : 800/Math.sqrt(2),
	drawYAxis : 3,
	drawYAxisNumbers : true,
	drawXAxis : 3,
	drawXAxisNumbers : true,
	drawGrid : true,
	showValues : false,
	drawTitle : true,
	title : "64-QAM spectrum",
	drawXAxisTitle : true,
	xAxisTitle : "Discrete frequency (f)"
};

var graph = new Graph(options);

var N = 1024, // Buffer length
	buffer = new Array(N);

var freq = 4000, // Analog signal frequency
	Ts = 0.001; // Symbol time

var sr = 16000; // Sample rate

var updateInterval = 0.025; // In seconds

var Sx = [], // Spectrum x axis points
	x = new Array(512),	// Buffer x axis points
	Sy = new Array(512), // Spectrum y axis points
	F; // Stores the resulting FFT

for (var j = 0; j < N; j++) {
	buffer[j] = 0;
	x[j] = j;
	Sx[j] = j/N;
}

function getSymbol (n) {

	var i = Math.random(), 
		q = Math.random();

	if (n == 2) {

		i = (i < 0.5) ? -(1/2) : 1/2;
		q = (q < 0.5) ? -(1/2) : 1/2;

	} else if (n == 4) {

		if (0 <= i && i < 0.25) i = -(3/2);
		else if (0.25 <= i && i < 0.5) i = -(1/2);
		else if (0.5 <= i && i < 0.75) i = 1/2;
		else i = 3/2;

		if (0 <= q && q < 0.25) q = -(3/2);
		else if (0.25 <= q && q < 0.5) q = -(1/2);
		else if (0.5 <= q && q < 0.75) q = 1/2;
		else q = 3/2;

	} else if (n == 8) {

		if (0 <= i && i < 0.125) i = -(7/2);
		else if (0.125 <= i && i < 0.25) i = -(5/2);
		else if (0.25 <= i && i < 0.375) i = -(3/2);
		else if (0.375 <= i && i < 0.5) i = -(1/2);
		else if (0.5 <= i && i < 0.625) i = 1/2;
		else if (0.625 <= i && i < 0.75) i = 3/2;
		else if (0.75 <= i && i < 0.875) i = 5/2;
		else i = 7/2;

		if (0 <= q && q < 0.125) q = -(7/2);
		else if (0.125 <= q && q < 0.25) q = -(5/2);
		else if (0.25 <= q && q < 0.375) q = -(3/2);
		else if (0.375 <= q && q < 0.5) q = -(1/2);
		else if (0.5 <= q && q < 0.625) q = 1/2;
		else if (0.625 <= q && q < 0.75) q = 3/2;
		else if (0.75 <= q && q < 0.875) q = 5/2;
		else q = 7/2;

	} else {

		i = 0; q = 0;

	}

	return [i, q];

}

function qam(i, q, f, t) {

	return (i*Math.cos(Math.PI*2*f*t) - q*Math.sin(2*Math.PI*f*t));

}

function fft(s) {

	var a = 0;
	var e = [], E = []; // even
	var o = [], O = []; // odd
	var N = s.length;

	var r = new Array(N); // result

	if (N > 1) {
		for (var n = 0; n < N; n++) {
			if (n%2 === 0) {
				e.push(s[n]);
			} else {
				o.push(s[n]);
			}
		}

		E = fft(e); O = fft(o);

		for (var k = 0; k < N/2; k++) {
			a = 2*Math.PI*k/N;
			// (E[k][0] + jE[k][1]) + (cos(a) - jsin(a))*(O[k][0] + jO[k][1])
			r[k] = [
				E[k][0] + (Math.cos(a)*O[k][0] + Math.sin(a)*O[k][1]),
				E[k][1] + (Math.cos(a)*O[k][1] - Math.sin(a)*O[k][0])];
			r[k + N/2] = [
				E[k][0] - (Math.cos(a)*O[k][0] + Math.sin(a)*O[k][1]),
				E[k][1] - (Math.cos(a)*O[k][1] - Math.sin(a)*O[k][0])];
		}

	} else {
		r[0] = [s[0], 0];
	}

	return r;

}

function loop() {

	var levels = 8, // number of levels
		iq; // In-phase and Quadrature components

	var symbols = updateInterval/Ts,
		samplesPerSymbol = Ts*sr;

	for (var j = 0; j < symbols; j++) {

		iq = getSymbol(levels);

		// sample that symbol
		for (var n = 0; n < samplesPerSymbol; n++) {

			if (buffer.length >= N) {
				buffer.splice(0, 1);
			}

			buffer.push(qam(iq[0], iq[1], freq, n/sr));

		}

	}

	F = fft(buffer);

	// Spectrum
	for (var j = 0; j < F.length; j++) {
		Sy[j] = 10*Math.log(Math.sqrt(Math.pow(F[j][0], 2) + Math.pow(F[j][1], 2)));
	}

	// Plot the time serie
	//graph.clear(options).plot(buffer, x, [256, 511], [-4, 4]);

	// Plot the spectrum
	graph.clear(options).plot(Sy, Sx, [0,0.5], [0,70]);

	// Constellation
	//graph.clear(options).points([iq[0]], [iq[1]], [-4, 4], [-4, 4]);

	setTimeout(loop, 1000*updateInterval);

}

loop();