Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/autonoma.ca.git
/* Copyright 2024 White Magic Software, Ltd. -- All rights reserved.
 *
 * SPDX-License-Identifier: MIT
 */

/**
 * Determines the maximum value within an array.
 */
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;
};

/**
 * Determines the minimum value within an array.
 */
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;
};

/**
 * Responsible for graphing flight information.
 */
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;