Skip to content

Commit

Permalink
Combine plots
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardalee committed Jun 30, 2024
1 parent 8e23b73 commit 4b82e8d
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 18 deletions.
100 changes: 86 additions & 14 deletions examples/C/src/synchrophasors/Synchrophasors.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,60 @@
</head>
<body>
<h1>Synchrophasors</h1>

<div width="400">
<canvas id="plot"></canvas>
<p>
Phase measurement units (PMUs) periodically emit a phasor measurement.
In a normally functioning power grid, the phases in nearby PMUs are correlated and vary smoothly.
</p>
<h2>Ideal Readings</h2>
<p>
In the plot below, there are 50 PMUs, one of which is exhibiting a fault (number 42):
</p>
<div style="position: relative; height:400px; width:600px">
<canvas id="cleanPlot" width="400"></canvas>
</div>
<h2>Noisy Readings</h2>
<p>
In the plot below, phasor data experiences random network delays and the plot is updated as data arrives.
This results in the simultaneous display of inconsistent data (data from different times), which masks the fault at PMU 42.
</p>
<div style="height:400px; width:600px">
<canvas id="noisyPlot" width="400"></canvas>
</div>
<p id="message"></p>

<script>
// Number of points to plot.
const n = 50;
const ctx = document.getElementById('plot');
const ctx = document.getElementById('cleanPlot');

chart = new Chart(ctx, {
cleanChart = new Chart(ctx, {
type: 'bar',
data: {
labels: [...Array(n).keys()],
datasets: [{
label: 'real part',
data: new Array(n).fill(0),
borderWidth: 1
}, {
label: 'imaginary part',
data: new Array(n).fill(0),
borderWidth: 1
}]
},
options: {
animation: false,
scales: {
y: {
beginAtZero: true,
min: -100,
max: 100
}
}
}
});

const ctx2 = document.getElementById('noisyPlot');
noisyChart = new Chart(ctx2, {
type: 'bar',
data: {
labels: [...Array(n).keys()],
Expand Down Expand Up @@ -46,30 +88,60 @@ <h1>Synchrophasors</h1>
window.onload = function() {

// Create the web socket connection for the data source.
const socket = new WebSocket('ws://localhost:8080', 'ws');
const cleanSocket = new WebSocket('ws://localhost:8080', 'ws');

socket.addEventListener('open', (event) => {
cleanSocket.addEventListener('open', (event) => {
console.log('WebSocket connection established for synchrophasor display');
});

socket.onerror = function(error) {
cleanSocket.onerror = function(error) {
console.log('Synchrophasor WebSocket Error: ' + error);
};

cleanSocket.addEventListener('message', (event) => {
var message = event.data;
// console.log(`WebSocket message received: ${message}`);

try {
// console.log('"' + message + '"');
var data = JSON.parse(message);
for (let i = 0; i < data.length; i++) {
if (data[i][0] < cleanChart.data.datasets[0].data.length) { // Safety check.
cleanChart.data.datasets[0].data[data[i][0]] = data[i][1][0] // Real part.
cleanChart.data.datasets[1].data[data[i][0]] = data[i][1][1] // Imaginary part.
}
}
cleanChart.update();
} catch(error) {
console.error(error);
}
});

// Create the web socket connection for the data source.
const noisySocket = new WebSocket('ws://localhost:8081', 'ws');

noisySocket.addEventListener('open', (event) => {
console.log('WebSocket connection established for noisy synchrophasor display');
});

noisySocket.onerror = function(error) {
console.log('Synchrophasor WebSocket Error: ' + error);
};

socket.addEventListener('message', (event) => {
noisySocket.addEventListener('message', (event) => {
var message = event.data;
console.log(`WebSocket message received: ${message}`);
// console.log(`WebSocket message received: ${message}`);

try {
// console.log('"' + message + '"');
var data = JSON.parse(message);
for (let i = 0; i < data.length; i++) {
if (data[i][0] < chart.data.datasets[0].data.length) { // Safety check.
chart.data.datasets[0].data[data[i][0]] = data[i][1][0] // Real part.
chart.data.datasets[1].data[data[i][0]] = data[i][1][1] // Imaginary part.
if (data[i][0] < noisyChart.data.datasets[0].data.length) { // Safety check.
noisyChart.data.datasets[0].data[data[i][0]] = data[i][1][0] // Real part.
noisyChart.data.datasets[1].data[data[i][0]] = data[i][1][1] // Imaginary part.
}
}
chart.update();
noisyChart.update();
} catch(error) {
console.error(error);
}
Expand Down
17 changes: 13 additions & 4 deletions examples/C/src/synchrophasors/Synchrophasors.lf
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ preamble {=
main reactor(n: int = 50) {
s = new[n] PhaseMeasurementUnit(drift=0.1, period = 100 ms, faulty_index = 42)
t = new[n] RandomDelay(average = 1 s)
o = new Observer(n=n)
clean = new Observer(
n=n,
hostport = 8080,
initial_file = {= LF_SOURCE_DIRECTORY LF_FILE_SEPARATOR "Synchrophasors.html" =}
)
noisy = new Observer(n=n, hostport = 8081)
s.phasor -> clean.phasors
s.phasor -> t.in
t.out -> o.phasors
t.out -> noisy.phasors
}

reactor RandomDelay(average: time = 1 sec, bank_index: int = 0) extends Random {
Expand Down Expand Up @@ -96,7 +102,9 @@ reactor PhaseMeasurementUnit(
*/
reactor Observer(
n: int = 2, // Number of inputs
period: time = 100 ms
period: time = 100 ms,
hostport: int = 8080,
initial_file: string = {= NULL =}
) {
input[n] phasors: timestamped_complex_t
state connected: bool = false
Expand All @@ -105,7 +113,8 @@ reactor Observer(
state last_sent: time = 0

w = new WebSocketServerString(
initial_file = {= LF_SOURCE_DIRECTORY LF_FILE_SEPARATOR "Synchrophasors.html" =})
hostport = hostport,
initial_file = initial_file)

reaction(phasors) -> w.in_dynamic {=
if (self->connected) {
Expand Down

0 comments on commit 4b82e8d

Please sign in to comment.