Array.prototype.max = function () {
let maxVal = this[0];
for (let i = 1; i < this.length; i++) {
if (this[i] > maxVal) {
maxVal = this[i];
}
}
return maxVal;
};
Array.prototype.min = function () {
let minVal = this[0];
for (let i = 1; i < this.length; i++) {
if (this[i] < minVal) {
minVal = this[i];
}
}
return minVal;
};
class Telemetry {
constructor(measureName) {
this.times = [];
this.values = [];
this.id = measureName.toLowerCase().replace(/\s+/g, '-');
const plotContainer = document.getElementById('plots');
const existingDiv = document.getElementById(this.id);
if (existingDiv) {
plotContainer.removeChild(existingDiv);
}
const plotDiv = document.createElement('div');
plotDiv.id = this.id;
plotDiv.style.border = '1px solid #ccc';
plotDiv.style.borderRadius = '10px';
plotDiv.style.padding = '10px';
plotDiv.style.marginBottom = '20px';
plotContainer.appendChild(plotDiv);
}
record(time, value) {
this.times.push(time);
this.values.push(value);
}
max() {
return this.values.max();
}
min() {
return this.values.min();
}
plot(plotName, yLabel) {
const data = this.times.map((time, index) => ({
time: time,
value: this.values[index]
}));
const minYValue = d3.min(data, d => d.value);
const maxYValue = d3.max(data, d => d.value);
const numDigits = Math.max(minYValue.toFixed(0).length, maxYValue.toFixed(0).length);
const margin = {
top: 30,
right: 30,
bottom: 50,
left: 40 + (numDigits * 10)
},
width = 800 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
const svg = d3.select(`#${this.id}`).append('svg')
.attr('viewBox', `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.attr('preserveAspectRatio', 'xMidYMid meet')
.style('width', '100%')
.style('height', '100%')
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
const x = d3.scaleLinear()
.domain([d3.min(data, d => d.time), d3.max(data, d => d.time)])
.range([0, width]);
const y = d3.scaleLinear()
.domain([minYValue, maxYValue])
.range([height, 0]);
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x));
svg.append('g')
.call(d3.axisLeft(y));
svg.append('path')
.datum(data)
.attr('fill', 'none')
.attr('stroke', 'steelblue')
.attr('stroke-width', 1.5)
.attr('d', d3.line()
.x(d => x(d.time))
.y(d => y(d.value))
);
svg.append('text')
.attr('x', width / 2)
.attr('y', -margin.top / 2 + 5)
.attr('text-anchor', 'middle')
.style('font-family', 'sans-serif')
.style('font-size', '16px')
.style('fill', '#333')
.text(plotName.replace(/_/g, ' '));
svg.append('text')
.attr('x', width / 2)
.attr('y', height + margin.bottom - 10)
.attr('text-anchor', 'middle')
.style('font-family', 'sans-serif')
.style('fill', '#333')
.text('Time (s)');
const formattedYLabel = yLabel.replace('^2', '²');
svg.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', -margin.left + 20 + (numDigits * 3))
.attr('x', -height / 2)
.attr('text-anchor', 'middle')
.style('font-family', 'sans-serif')
.style('fill', '#333')
.text(formattedYLabel);
}
}
export default Telemetry;