Javascript  

 

 

 

 

 

Plot Constellation with Channel/Precoding Matrix

 

this code is created first by chatGPT on Jan 28 2023 (meaning using chatGPT 3.5) and then modified a little bit myself. The initial request that I put into chatGPT is as follows :

 

The code in this note is made by manual modification to the script in this note.

 

Usage : Click on [Constellation] button. Whenever you click on it, you will get different constellation. If you roll over each of the text box, you will get spin buttons on the right end of the textbox. Click on up/down arrow on the spin button to change the values.

 

      NOTE : The plot shown above is the result of the expression : M . transpose([sig1, sig2]),

      where sig1 and sig2 is the complex arragy. M is 2x2 complex matrix

      NOTE : You can put any 2x2 matrix in Matrix field, but sometimes it may be challenging to put them in proper format which does not generate errors. For your convinience, I put a couple of sample Matrix you may try:

        [[0.5+0.0i,0.5+0.0i],[0.5+0.0i,0.5+0.0i]]

        [[1.0+0.0i,0.0+00i],[0.0+0.0i,1.0-0.0i]]

        [[0.5+0.0i,0.5+0.0i],[0.5+0.0i,0.5+0.0i]]

        [[0.25+0.25i,0.25+0.25i],[0.25+0.25i,0.25-0.25i]]

      NOTE : It may look as if this is for one Tx stream and one Rx stream, but in reality it is for two Tx stream and two Rx stream. The input(left plot) constellation has two colors blue and red color, but you would see only one color since the two color are superimposed. You would see two colors if you increase noise level. Same applies to output (right plot) constellation as well : purple and yellow.

 

NOTE : The part highlighted in red is the one that I added. The main purpose of this script is to show how to do complex number operation in math.js library.

ConstellationM.html

<!DOCTYPE html>

<html>

 

<head>

    <title>Constellation Plot</title>

 

</head>

 

<body>

        <table align="center" style="border: 1px solid black;">

        <tr>

            <td colspan="2">

                <label for="NoOfSample">Number of samples</label>

                <input type="number" id="NoOfSample" name="NoOfSample" value="4000" min="1" step="100" onClick="plotSignal()">

                <button id="Constellation" onClick="plotSignal()">Constellation</button>

            </td>

        </tr>

        <tr>

            <td colspan="2">

                <label for="selectMod">Modulation</label>

                <select id="selectMod" name="selectMod" onchange="plotSignal()">

                  <option value="modQAM">QAM</option>

                  <option value="mod16QAM">16QAM</option>

                  <option value="mod64QAM">64QAM</option>

                  <option value="mod256QAM">256QAM</option>

                </select>

            </td>

        </tr>

        <tr>

            <td colspan="2">

                <label for="Noise">Gaussian Noise (in dB)</label>

                <input type="number" id="Noise" name="Noise" value="-35" step="1" onClick="plotSignal()">

            </td>

        </tr>

        <tr>

            <td colspan="2">

                <label for="Matrix">Matrix </label>

                <input type="text" id="Matrix" size=40 value="[[0.5+0.0i,0.5+0.0i],[0.5+0.0i,0.5+0.0i]]">

            </td>

        </tr>

        <tr style="border: 1px solid black; ">

            <td style="border: 1px solid black;" width="300">

                <canvas id="constellationCanvas" width="300" height="300" style="border:2px solid #000000;"></canvas>

            </td>

            <td style="border: 1px solid black;" width="300">

                <canvas id="constellationCanvasResult" width="300" height="300" style="border:2px solid #000000;"></canvas>

            </td>

        </tr>

    </table>

    <script type="text/javascript" src="mathjs/math.js"></script>

    <script type="text/javascript" src="ConstellationM.js"></script>

</body>

 

</html>

 

ConstellationM.js

const plotSignal = () => {

 

    // Get the canvas element and context

    const canvas = document.getElementById('constellationCanvas');

    const canvasResult = document.getElementById('constellationCanvasResult');

    const noise = document.getElementById('Noise');

    const Mstring = document.getElementById('Matrix').value;

    const ctx = canvas.getContext('2d');

    const ctxRes = canvasResult.getContext('2d');

    var selectMOD = document.getElementById("selectMod");

    var selectedMod = selectMOD.options[selectMOD.selectedIndex].text;

    var modQ = 0;

 

    switch (selectedMod) {

        case "QAM":

            modQ = 2;

            break;

        case "16QAM":

            modQ = 4;

            break;

        case "64QAM":

            modQ = 6;

            break;

        case "256QAM":

            modQ = 8;

            break;

    }

 

    let numbers = math.evaluate(Mstring);

    let c11 = math.complex(numbers._data[0][0].re,numbers._data[0][0].im);

    let c21 = math.complex(numbers._data[1][0].re,numbers._data[1][0].im);

    let c12 = math.complex(numbers._data[0][1].re,numbers._data[0][1].im);

    let c22 = math.complex(numbers._data[1][1].re,numbers._data[1][1].im);

    

    

    ctx.fillStyle = 'white';

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    ctxRes.fillStyle = 'red';

    ctxRes.clearRect(0, 0, canvasResult.width, canvasResult.height);

 

    // Get the number of samples from the textbox

    const numSamples = document.getElementById('NoOfSample').value;

 

    // Generate an array of random complex numbers

 

    let noiseLevel_dB = noise.value;

    let noiseLevel = Math.pow(10, noiseLevel_dB/20);

    let signal1 = [];

    let signal2 = [];

   

 

    for (let i = 0; i < numSamples; i++) {

        let step = 2/(modQ-1);

        let lbound = -1.0;

        let real1 = lbound + step*getRandomInt(0, modQ-1)  + gaussianRandom(0, noiseLevel);

        let imag1 = lbound + step*getRandomInt(0, modQ-1) + gaussianRandom(0, noiseLevel);

        signal1.push(math.complex(real1, imag1));

        let real2 = lbound + step*getRandomInt(0, modQ-1)  + gaussianRandom(0, noiseLevel);

        let imag2 = lbound + step*getRandomInt(0, modQ-1) + gaussianRandom(0, noiseLevel);

        signal2.push(math.complex(real2, imag2));

    }

 

    let real,imag;

    // Scale the canvas

    ctx.save();

    ctx.lineWidth = 1/(canvas.width/4);

    ctx.scale(canvas.width / 4, -canvas.height / 4);

    ctx.translate(2, -2);

 

    ctxRes.save();

    ctxRes.lineWidth = 1/(canvasResult.width/4);

    ctxRes.scale(canvasResult.width / 4, -canvasResult.height / 4);

    ctxRes.translate(2, -2);

 

    // Draw the grid

    ctx.strokeStyle = 'gray';

    ctx.beginPath();

    for (let i = -2.0; i <= 2.0; i += 0.2) {

        ctx.moveTo(i, -1.98);

        ctx.lineTo(i, 1.98);

        ctx.moveTo(-1.98, i);

        ctx.lineTo(1.98, i);

    }

    ctx.stroke();

 

    ctxRes.strokeStyle = 'gray';

    ctxRes.beginPath();

    for (let i = -2.0; i <= 2.0; i += 0.2) {

        ctxRes.moveTo(i, -1.98);

        ctxRes.lineTo(i, 1.98);

        ctxRes.moveTo(-1.98, i);

        ctxRes.lineTo(1.98, i);

    }

    ctxRes.stroke();

 

    // Draw the X and Y axis

    ctx.strokeStyle = 'black';

    ctx.beginPath();

    ctx.moveTo(-2, 0);

    ctx.lineTo(2, 0);

    ctx.moveTo(0, -2);

    ctx.lineTo(0, 2);

    ctx.stroke();

 

    ctxRes.strokeStyle = 'black';

    ctxRes.beginPath();

    ctxRes.moveTo(-2, 0);

    ctxRes.lineTo(2, 0);

    ctxRes.moveTo(0, -2);

    ctxRes.lineTo(0, 2);

    ctxRes.stroke();

 

    // Plot the signal

    ctx.fillStyle = 'blue';

 

    for (let i = 0; i < numSamples; i++) {

        let s = signal1[i];

        real = s.re;

        imag = s.im;

        ctx.fillRect(real-0.025,imag-0.025,0.05,0.05);

    }

 

    ctx.fillStyle = 'red';

 

    for (let i = 0; i < numSamples; i++) {

        let s = signal2[i];

        real = s.re;

        imag = s.im;

        ctx.fillRect(real-0.025,imag-0.025,0.05,0.05);

    }

 

    ctx.restore();

 

    ctxRes.fillStyle = 'yellow';

 

    for (let i = 0; i < numSamples; i++) {

        let s = math.add(math.multiply(c11,signal1[i]),math.multiply(c12,signal2[i]));

        real = s.re;

        imag = s.im;

        ctxRes.fillRect(real-0.025,imag-0.025,0.05,0.05);

    }

 

    ctxRes.fillStyle = 'purple';

 

    for (let i = 0; i < numSamples; i++) {

        let s = math.add(math.multiply(c21,signal1[i]),math.multiply(c22,signal2[i]));

        real = s.re;

        imag = s.im;

        ctxRes.fillRect(real-0.025,imag-0.025,0.05,0.05);

    }

 

    ctxRes.restore();

}

 

function gaussianRandom(mean, stddev) {

    var u1 = Math.random();

    var u2 = Math.random();

    var z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);

    return z0 * stddev + mean;

}

 

function getRandomInt(min, max) {

    min = Math.ceil(min);

    max = Math.floor(max);

    return Math.floor(Math.random() * (max - min + 1)) + min;

}