Now for something else completely different. A realtime plot can be a crowd pleaser, but it is considered very rude to rapidly request the same seismogram over and over from the FDSN dataselect web service, and so we will use a web socket to the IRIS ringserver using the DataLink protocol.
First we need to set up a bunch of variables we will use to keep track of the realtime data. The timerInterval is set so that hopefully it updates the display just often enough to move the image over by one pixel. We will also need an error handling function.
const matchPattern = `CO_JSC_00_HH./MSEED`;
seisplotjs.d3.select('span#channel').text(matchPattern);
const duration = seisplotjs.moment.duration(5, 'minutes');
const timeWindow = new seisplotjs.util.StartEndDuration(null, null, duration);
const seisPlotConfig = new seisplotjs.seismographconfig.SeismographConfig();
seisPlotConfig.wheelZoom = false;
let graphList = new Map();
let numPackets = 0;
let paused = false;
let stopped = true;
let redrawInProgress = false;
let realtimeDiv = seisplotjs.d3.select("div#realtime");
let rect = realtimeDiv.node().getBoundingClientRect();
let timerInterval = duration.asMilliseconds()/
(rect.width)-margin.left-margin.right);
console.log("start time with interval "+timerInterval);
while (timerInterval < 100) { timerInterval *= 2;}
const errorFn = function(error) {
console.assert(false, error);
if (datalink) {datalink.close();}
seisplotjs.d3.select("p#error").text("Error: "+error);
};
And a function to handle each datalink packet as it arrives. In this case all datalink packets should contain a single miniseed record, but there is nothing in the datalink protocol that prevents sending other types of data as the payload.
const packetHandler = function(packet) {
if (packet.isMiniseed()) {
numPackets++;
seisplotjs.d3.select("span#numPackets").text(numPackets);
let seisSegment = seisplotjs.miniseed.createSeismogramSegment(packet.miniseed);
const codes = seisSegment.codes();
let seisPlot = graphList.get(codes);
if ( ! seisPlot) {
let seismogram = new seisplotjs.seismogram.Seismogram( [ seisSegment ]);
let seisData = seisplotjs.seismogram.SeismogramDisplayData.fromSeismogram(seismogram);
let plotDiv = realtimeDiv.append("div").classed("seismograph", true);
let myseisPlotConfig = seisPlotConfig.clone();
myseisPlotConfig.title = codes;
seisPlot = new seisplotjs.seismograph.Seismograph(plotDiv, myseisPlotConfig, seisData);
graphList.set(codes, seisPlot);
console.log(`new plot: ${codes}`)
} else {
seisPlot.seisDataList[0].seismogram.append(seisSegment);
}
seisPlot.draw();
} else {
console.log(`not a mseed packet: ${packet.streamId}`)
}
};
Now we create the actual Datalink connection to the IRIS ringserver.
const datalink = new seisplotjs.datalink.DataLinkConnection(
seisplotjs.datalink.IRIS_RINGSERVER_URL,
packetHandler,
errorFn);
Here is the timer that will periodically refresh the displays.
let timer = seisplotjs.d3.interval(function(elapsed) {
if ( paused || redrawInProgress) {
return;
}
redrawInProgress = true;
window.requestAnimationFrame(timestamp => {
try {
seisPlotConfig.fixedTimeScale = new seisplotjs.util.StartEndDuration(null, null, duration);
graphList.forEach(function(value, key) {
value.calcTimeScaleDomain();
value.calcAmpScaleDomain();
value.draw();
});
redrawInProgress = false;
} catch(err) {
console.assert(false, err);
}
});
}, timerInterval);
We wire up the pause button.
seisplotjs.d3.select("button#pause").on("click", function(d) {
togglePause( );
});
let togglePause = function() {
paused = ! paused;
if (paused) {
seisplotjs.d3.select("button#pause").text("Play");
} else {
seisplotjs.d3.select("button#pause").text("Pause");
}
}
And wire up the disconnect button
seisplotjs.d3.select("button#disconnect").on("click", function(d) {
toggleConnect();
});
let toggleConnect = function() {
stopped = ! stopped;
if (stopped) {
if (datalink) {
datalink.endStream();
datalink.close();
}
seisplotjs.d3.select("button#disconnect").text("Reconnect");
} else {
if (datalink) {
datalink.connect()
.then(serverId => {
console.log(`id response: ${serverId}`);
return datalink.match(matchPattern);
}).then(response => {
console.log(`match response: ${response}`)
return datalink.positionAfter(timeWindow.start);
}).then(response => {
console.log(`positionAfter response: ${response}`)
return datalink.stream();
}).catch( function(error) {
seisplotjs.d3.select("div#debug").append('p').html("Error: " +error);
console.assert(false, error);
});
}
seisplotjs.d3.select("button#disconnect").text("Disconnect");
}
}
And then we start it going!
toggleConnect();
Previous: Helicorder
Next: ...and more