diff --git a/dependencies/machine/machine.js b/dependencies/machine/machine.js
new file mode 100644
index 0000000..bbd2fab
--- /dev/null
+++ b/dependencies/machine/machine.js
@@ -0,0 +1,814 @@
+/**
+ * @file machine.js
+ *
+ * Permission is hereby granted to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to use it for personal
+ * or non-commercial purposes, with the following restrictions:
+ *
+ * 1. **No Copying or Redistribution**: The Software or any of its parts may not
+ * be copied, merged, distributed, sublicensed, or sold without explicit
+ * prior written permission from the author.
+ *
+ * 2. **Commercial Use**: Any use of the Software for commercial purposes requires
+ * a valid license, obtainable only with the explicit consent of the author.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM,
+ * OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Ownership of this code remains solely with the original author. Unauthorized
+ * use of this Software is strictly prohibited.
+ *
+ * @summary A class to interact and manipulate machines with a non-euclidian curve
+ * @description A class to interact and manipulate machines with a non-euclidian curve
+ * @module machine
+ * @exports machine
+ * @version 0.1.0
+ * @since 0.1.0
+ *
+ * Author:
+ * - Rene De Ren
+ * Email:
+ * - rene@thegoldenbasket.nl
+ *
+ * Add functionality later
+ // -------- Operational Metrics -------- //
+maintenanceAlert: this.state.checkMaintenanceStatus()
+
+
+*/
+
+//load local dependencies
+const EventEmitter = require('events');
+const Logger = require('../../../generalFunctions/helper/logger');
+const State = require('../../../generalFunctions/helper/state/state');
+const Predict = require('../../../predict/dependencies/predict/predict_class');
+const { MeasurementContainer } = require('../../../generalFunctions/helper/measurements/index');
+const Interpolation = require('../../../predict/dependencies/predict/interpolation');
+
+//load all config modules
+const defaultConfig = require('../rotatingMachine/rotatingMachineConfig.json');
+const ConfigUtils = require('../../../generalFunctions/helper/configUtils');
+
+//load registration utility
+const ChildRegistrationUtils = require('../../../generalFunctions/helper/childRegistrationUtils');
+const ErrorMetrics = require('../../../generalFunctions/helper/nrmse/errorMetrics');
+
+class Machine {
+
+ /*------------------- Construct and set vars -------------------*/
+ constructor(machineConfig = {}, stateConfig = {}, errorMetricsConfig = {}) {
+
+ //basic setup
+ this.emitter = new EventEmitter(); // Own EventEmitter
+ this.configUtils = new ConfigUtils(defaultConfig);
+ this.config = this.configUtils.initConfig(machineConfig);
+
+ // Initialize measurements
+ this.measurements = new MeasurementContainer();
+ this.interpolation = new Interpolation();
+ this.child = {}; // object to hold child information so we know on what to subscribe
+
+ this.flowDrift = null;
+
+ // Init after config is set
+ this.logger = new Logger(this.config.general.logging.enabled, this.config.general.logging.logLevel, this.config.general.name);
+ this.state = new State(stateConfig, this.logger); // Init State manager and pass logger
+ this.errorMetrics = new ErrorMetrics(errorMetricsConfig, this.logger);
+
+ this.predictFlow = new Predict({ curve: this.config.asset.machineCurve.nq }); // load nq (x : ctrl , y : flow relationship)
+ this.predictPower = new Predict({ curve: this.config.asset.machineCurve.np }); // load np (x : ctrl , y : power relationship)
+ this.predictCtrl = new Predict({ curve: this.reverseCurve(this.config.asset.machineCurve.nq) }); // load reversed nq (x: flow, y: ctrl relationship)
+
+ this.currentMode = this.config.mode.current;
+ this.currentEfficiencyCurve = {};
+ this.cog = 0;
+ this.NCog = 0;
+ this.cogIndex = 0;
+ this.minEfficiency = 0;
+ this.absDistFromPeak = 0;
+ this.relDistFromPeak = 0;
+
+ this.state.emitter.on("positionChange", (data) => {
+ this.logger.debug(`Position change detected: ${data}`);
+ this.updatePosition();
+ });
+
+
+
+
+ //this.calcCog();
+
+
+ this.childRegistrationUtils = new ChildRegistrationUtils(this); // Child registration utility
+
+ }
+
+ // Method to assess drift using errorMetrics
+ assessDrift(measurement, processMin, processMax) {
+ this.logger.debug(`Assessing drift for measurement: ${measurement} processMin: ${processMin} processMax: ${processMax}`);
+ const predictedMeasurement = this.measurements.type(measurement).variant("predicted").position("downstream").getAllValues().values;
+ const measuredMeasurement = this.measurements.type(measurement).variant("measured").position("downstream").getAllValues().values;
+
+ if (!predictedMeasurement || !measuredMeasurement) return null;
+
+ return this.errorMetrics.assessDrift(
+ predictedMeasurement,
+ measuredMeasurement,
+ processMin,
+ processMax
+ );
+ }
+
+ reverseCurve(curve) {
+ const reversedCurve = {};
+ for (const [pressure, values] of Object.entries(curve)) {
+ reversedCurve[pressure] = {
+ x: [...values.y], // Previous y becomes new x
+ y: [...values.x] // Previous x becomes new y
+ };
+ }
+ return reversedCurve;
+ }
+
+ // -------- Config -------- //
+ updateConfig(newConfig) {
+ this.config = this.configUtils.updateConfig(this.config, newConfig);
+ }
+
+ // -------- Mode and Input Management -------- //
+ isValidSourceForMode(source, mode) {
+ const allowedSourcesSet = this.config.mode.allowedSources[mode] || [];
+ return allowedSourcesSet.has(source);
+ }
+
+ isValidActionForMode(action, mode) {
+ const allowedActionsSet = this.config.mode.allowedActions[mode] || [];
+ return allowedActionsSet.has(action);
+ }
+
+ async handleInput(source, action, parameter) {
+ if (!this.isValidSourceForMode(source, this.currentMode)) {
+ let warningTxt = `Source '${source}' is not valid for mode '${this.currentMode}'.`;
+ this.logger.warn(warningTxt);
+ return {status : false , feedback: warningTxt};
+ }
+
+
+ this.logger.info(`Handling input from source '${source}' with action '${action}' in mode '${this.currentMode}'.`);
+ try {
+ switch (action) {
+ case "execSequence":
+ await this.executeSequence(parameter);
+ //recalc flow and power
+ this.updatePosition();
+ break;
+ case "execMovement":
+ await this.setpoint(parameter);
+ break;
+ case "flowMovement":
+ // Calculate the control value for a desired flow
+ const pos = this.calcCtrl(parameter);
+ // Move to the desired setpoint
+ await this.setpoint(pos);
+ break;
+ case "emergencyStop":
+ this.logger.warn(`Emergency stop activated by '${source}'.`);
+ await this.executeSequence("emergencyStop");
+ break;
+ case "statusCheck":
+ this.logger.info(`Status Check: Mode = '${this.currentMode}', Source = '${source}'.`);
+ break;
+ default:
+ this.logger.warn(`Action '${action}' is not implemented.`);
+ break;
+ }
+ this.logger.debug(`Action '${action}' successfully executed`);
+ return {status : true , feedback: `Action '${action}' successfully executed.`};
+ } catch (error) {
+ this.logger.error(`Error handling input: ${error}`);
+ }
+ }
+
+ setMode(newMode) {
+ const availableModes = defaultConfig.mode.current.rules.values.map(v => v.value);
+ if (!availableModes.includes(newMode)) {
+ this.logger.warn(`Invalid mode '${newMode}'. Allowed modes are: ${availableModes.join(', ')}`);
+ return;
+ }
+
+ this.currentMode = newMode;
+ this.logger.info(`Mode successfully changed to '${newMode}'.`);
+ }
+
+ // -------- Sequence Handlers -------- //
+ async executeSequence(sequenceName) {
+
+ const sequence = this.config.sequences[sequenceName];
+
+ if (!sequence || sequence.size === 0) {
+ this.logger.warn(`Sequence '${sequenceName}' not defined.`);
+ return;
+ }
+
+ if (this.state.getCurrentState() == "operational" && sequenceName == "shutdown") {
+ this.logger.info(`Machine will ramp down to position 0 before performing ${sequenceName} sequence`);
+ await this.setpoint(0);
+ }
+
+ this.logger.info(` --------- Executing sequence: ${sequenceName} -------------`);
+
+ for (const state of sequence) {
+ try {
+ await this.state.transitionToState(state);
+ // Update measurements after state change
+
+ } catch (error) {
+ this.logger.error(`Error during sequence '${sequenceName}': ${error}`);
+ break; // Exit sequence execution on error
+ }
+ }
+ }
+
+ async setpoint(setpoint) {
+
+ try {
+ // Validate setpoint
+ if (typeof setpoint !== 'number' || setpoint < 0) {
+ throw new Error("Invalid setpoint: Setpoint must be a non-negative number.");
+ }
+
+ // Move to the desired setpoint
+ await this.state.moveTo(setpoint);
+
+ } catch (error) {
+ console.error(`Error setting setpoint: ${error}`);
+ }
+ }
+
+ // Calculate flow based on current pressure and position
+ calcFlow(x) {
+ const state = this.state.getCurrentState();
+
+ if (!["operational", "accelerating", "decelerating"].includes(state)) {
+ this.measurements.type("flow").variant("predicted").position("downstream").value(0);
+ this.logger.debug(`Machine is not operational. Setting predicted flow to 0.`);
+ return 0;
+ }
+
+ //this.predictFlow.currentX = x; Decrepated
+ const cFlow = this.predictFlow.y(x);
+ this.measurements.type("flow").variant("predicted").position("downstream").value(cFlow);
+ //this.logger.debug(`Calculated flow: ${cFlow} for pressure: ${this.getMeasuredPressure()} and position: ${x}`);
+ return cFlow;
+
+ }
+
+ // Calculate power based on current pressure and position
+ calcPower(x) {
+ const state = this.state.getCurrentState();
+ if (!["operational", "accelerating", "decelerating"].includes(state)) {
+ this.measurements.type("power").variant("predicted").position('upstream').value(0);
+ this.logger.debug(`Machine is not operational. Setting predicted power to 0.`);
+ return 0;
+ }
+
+ //this.predictPower.currentX = x; Decrepated
+ const cPower = this.predictPower.y(x);
+ this.measurements.type("power").variant("predicted").position('upstream').value(cPower);
+ //this.logger.debug(`Calculated power: ${cPower} for pressure: ${this.getMeasuredPressure()} and position: ${x}`);
+ return cPower;
+ }
+
+ // calculate the power consumption using only flow and pressure
+ inputFlowCalcPower(flow) {
+ this.predictCtrl.currentX = flow;
+ const cCtrl = this.predictCtrl.y(flow);
+ this.predictPower.currentX = cCtrl;
+ const cPower = this.predictPower.y(cCtrl);
+ return cPower;
+ }
+
+ // Function to predict control value for a desired flow
+ calcCtrl(x) {
+
+ this.predictCtrl.currentX = x;
+ const cCtrl = this.predictCtrl.y(x);
+ this.measurements.type("ctrl").variant("predicted").position('upstream').value(cCtrl);
+ //this.logger.debug(`Calculated ctrl: ${cCtrl} for pressure: ${this.getMeasuredPressure()} and position: ${x}`);
+ return cCtrl;
+
+ }
+
+ // this function returns the pressure for calculations
+ getMeasuredPressure() {
+ const pressureDiff = this.measurements.type('pressure').variant('measured').difference();
+
+ // Both upstream & downstream => differential
+ if (pressureDiff != null) {
+ this.logger.debug(`Pressure differential: ${pressureDiff.value}`);
+ this.predictFlow.fDimension = pressureDiff.value;
+ this.predictPower.fDimension = pressureDiff.value;
+ this.predictCtrl.fDimension = pressureDiff.value;
+ //update the cog
+ const { cog, minEfficiency } = this.calcCog();
+ // calc efficiency
+ const efficiency = this.calcEfficiency(this.predictPower.outputY, this.predictFlow.outputY, "predicted");
+ //update the distance from peak
+ this.calcDistanceBEP(efficiency,cog,minEfficiency);
+
+ return pressureDiff.value;
+ }
+
+ // get downstream
+ const downstreamPressure = this.measurements.type('pressure').variant('measured').position('downstream').getCurrentValue();
+
+ // Only downstream => use it, warn that it's partial
+ if (downstreamPressure != null) {
+ this.logger.warn(`Using downstream pressure only for prediction: ${downstreamPressure} `);
+ this.predictFlow.fDimension = downstreamPressure;
+ this.predictPower.fDimension = downstreamPressure;
+ this.predictCtrl.fDimension = downstreamPressure;
+ //update the cog
+ const { cog, minEfficiency } = this.calcCog();
+ // calc efficiency
+ const efficiency = this.calcEfficiency(this.predictPower.outputY, this.predictFlow.outputY, "predicted");
+ //update the distance from peak
+ this.calcDistanceBEP(efficiency,cog,minEfficiency);
+ return downstreamPressure;
+ }
+
+ this.logger.error(`No valid pressure measurements available to calculate prediction using last known pressure`);
+
+ //set default at 0 => lowest pressure possible
+ this.predictFlow.fDimension = 0;
+ this.predictPower.fDimension = 0;
+ this.predictCtrl.fDimension = 0;
+ //update the cog
+ const { cog, minEfficiency } = this.calcCog();
+ // calc efficiency
+ const efficiency = this.calcEfficiency(this.predictPower.outputY, this.predictFlow.outputY, "predicted");
+ //update the distance from peak
+ this.calcDistanceBEP(efficiency,cog,minEfficiency);
+ return 0;
+ }
+
+ handleMeasuredFlow() {
+ const flowDiff = this.measurements.type('flow').variant('measured').difference();
+
+ // If both are present
+ if (flowDiff != null) {
+ // In theory, mass flow in = mass flow out, so they should match or be close.
+ if (flowDiff.value < 0.001) {
+ // flows match within tolerance
+ this.logger.debug(`Flow match: ${flowDiff.value}`);
+ return flowDiff.value;
+ } else {
+ // Mismatch => decide how to handle. Maybe take the average?
+ // Or bail out with an error. Example: we bail out here.
+ this.logger.error(`Something wrong with down or upstream flow measurement. Bailing out!`);
+ return null;
+ }
+ }
+
+ // get
+ const upstreamFlow = this.measurements.type('pressure').variant('measured').position('upstream').getCurrentValue();
+
+ // Only upstream => might still accept it, but warn
+ if (upstreamFlow != null) {
+ this.logger.warn(`Only upstream flow is present. Using it but results may be incomplete!`);
+ return upstreamFlow;
+ }
+
+ // get
+ const downstreamFlow = this.measurements.type('pressure').variant('measured').position('downstream').getCurrentValue();
+
+ // Only downstream => might still accept it, but warn
+ if (downstreamFlow != null) {
+ this.logger.warn(`Only downstream flow is present. Using it but results may be incomplete!`);
+ return downstreamFlow;
+ }
+
+ // Neither => error
+ this.logger.error(`No upstream or downstream flow measurement. Bailing out!`);
+ return null;
+ }
+
+ handleMeasuredPower() {
+ const power = this.measurements.type("power").variant("measured").position("upstream").getCurrentValue();
+ // If your system calls it "upstream" or just a single "value", adjust accordingly
+
+ if (power != null) {
+ this.logger.debug(`Measured power: ${power}`);
+ return power;
+ } else {
+ this.logger.error(`No measured power found. Bailing out!`);
+ return null;
+ }
+ }
+
+ updatePressure(variant,value,position) {
+
+ switch (variant) {
+ case ("measured"):
+ //only update when machine is in a state where it can be used
+ if (this.state.getCurrentState() == "operational" || this.state.getCurrentState() == "accelerating" || this.state.getCurrentState() == "decelerating") {
+ // put value in measurements
+ this.measurements.type("pressure").variant("measured").position(position).value(value);
+ //when measured pressure gets updated we need some logic to fetch the relevant value which could be downstream or differential pressure
+ const pressure = this.getMeasuredPressure();
+ //update the flow power and cog
+ this.updatePosition();
+ this.logger.debug(`Measured pressure: ${pressure}`);
+ }
+ break;
+
+ default:
+ this.logger.warn(`Unrecognized variant '${variant}' for pressure update.`);
+ break;
+ }
+ }
+
+ updateFlow(variant,value,position) {
+
+ switch (variant) {
+ case ("measured"):
+ // put value in measurements
+ this.measurements.type("flow").variant("measured").position(position).value(value);
+ //when measured flow gets updated we need to push the last known value in the prediction measurements to keep them synced
+ this.measurements.type("flow").variant("predicted").position("downstream").value(this.predictFlow.outputY);
+ break;
+
+ case ("predicted"):
+ this.logger.debug('not doing anythin yet');
+ break;
+
+ default:
+ this.logger.warn(`Unrecognized variant '${variant}' for flow update.`);
+ break;
+ }
+ }
+
+ updateMeasurement(variant, subType, value, position) {
+ this.logger.debug(`---------------------- updating ${subType} ------------------ `);
+ switch (subType) {
+ case "pressure":
+ // Update pressure measurement
+ this.updatePressure(variant,value,position);
+ break;
+ case "flow":
+ this.updateFlow(variant,value,position);
+ // Update flow measurement
+ this.flowDrift = this.assessDrift("flow", this.predictFlow.currentFxyYMin , this.predictFlow.currentFxyYMax);
+ this.logger.debug(`---------------------------------------- `);
+ break;
+ case "power":
+ // Update power measurement
+ break;
+ default:
+ this.logger.error(`Type '${type}' not recognized for measured update.`);
+ return;
+ }
+ }
+
+ //what is the internal functions that need updating when something changes that has influence on this.
+ updatePosition() {
+ if (this.state.getCurrentState() == "operational" || this.state.getCurrentState() == "accelerating" || this.state.getCurrentState() == "decelerating") {
+
+ const currentPosition = this.state.getCurrentPosition();
+
+ // Update the predicted values based on the new position
+ const { cPower, cFlow } = this.calcFlowPower(currentPosition);
+
+ // Calc predicted efficiency
+ const efficiency = this.calcEfficiency(cPower, cFlow, "predicted");
+
+ //update the cog
+ const { cog, minEfficiency } = this.calcCog();
+
+ //update the distance from peak
+ this.calcDistanceBEP(efficiency,cog,minEfficiency);
+
+ }
+ }
+
+ calcDistanceFromPeak(currentEfficiency,peakEfficiency){
+ return Math.abs(currentEfficiency - peakEfficiency);
+ }
+
+ calcRelativeDistanceFromPeak(currentEfficiency,maxEfficiency,minEfficiency){
+ let distance = 1;
+ if(currentEfficiency != null){
+ distance = this.interpolation.interpolate_lin_single_point(currentEfficiency,maxEfficiency, minEfficiency, 0, 1);
+ }
+ return distance;
+ }
+
+ // Calculate the center of gravity for current pressure
+ calcCog() {
+
+ //fetch current curve data for power and flow
+ const { powerCurve, flowCurve } = this.getCurrentCurves();
+
+ const {efficiencyCurve, peak, peakIndex, minEfficiency } = this.calcEfficiencyCurve(powerCurve, flowCurve);
+
+ // Calculate the normalized center of gravity
+ const NCog = (flowCurve.y[peakIndex] - this.predictFlow.currentFxyYMin) / (this.predictFlow.currentFxyYMax - this.predictFlow.currentFxyYMin);
+
+ //store in object for later retrieval
+ this.currentEfficiencyCurve = efficiencyCurve;
+ this.cog = peak;
+ this.cogIndex = peakIndex;
+ this.NCog = NCog;
+ this.minEfficiency = minEfficiency;
+
+ return { cog: peak, cogIndex: peakIndex, NCog: NCog, minEfficiency: minEfficiency };
+
+ }
+
+ calcEfficiencyCurve(powerCurve, flowCurve) {
+
+ const efficiencyCurve = [];
+ let peak = 0;
+ let peakIndex = 0;
+ let minEfficiency = 0;
+
+ // Calculate efficiency curve based on power and flow curves
+ powerCurve.y.forEach((power, index) => {
+
+ // Get flow for the current power
+ const flow = flowCurve.y[index];
+
+ // higher efficiency is better
+ efficiencyCurve.push( Math.round( ( flow / power ) * 100 ) / 100);
+
+ // Keep track of peak efficiency
+ peak = Math.max(peak, efficiencyCurve[index]);
+ peakIndex = peak == efficiencyCurve[index] ? index : peakIndex;
+ minEfficiency = Math.min(...efficiencyCurve);
+
+ });
+
+ return { efficiencyCurve, peak, peakIndex, minEfficiency };
+
+ }
+
+ //calc flow power based on pressure and current position
+ calcFlowPower(x) {
+
+ // Calculate flow and power
+ const cFlow = this.calcFlow(x);
+ const cPower = this.calcPower(x);
+
+ return { cPower, cFlow };
+ }
+
+ calcEfficiency(power, flow, variant) {
+
+ if (power != 0 && flow != 0) {
+ // Calculate efficiency after measurements update
+ this.measurements.type("efficiency").variant(variant).position('downstream').value((flow / power));
+ } else {
+ this.measurements.type("efficiency").variant(variant).position('downstream').value(null);
+ }
+
+ return this.measurements.type("efficiency").variant(variant).position('downstream').getCurrentValue();
+
+ }
+
+ updateCurve(newCurve) {
+ this.logger.info(`Updating machine curve`);
+ const newConfig = { asset: { machineCurve: newCurve } };
+
+ //validate input of new curve fed to the machine
+ this.config = this.configUtils.updateConfig(this.config, newConfig);
+
+ //After we passed validation load the curves into their predictors
+ this.predictFlow.updateCurve(this.config.asset.machineCurve.nq);
+ this.predictPower.updateCurve(this.config.asset.machineCurve.np);
+ this.predictCtrl.updateCurve(this.reverseCurve(this.config.asset.machineCurve.nq));
+ }
+
+ getCompleteCurve() {
+ const powerCurve = this.predictPower.inputCurveData;
+ const flowCurve = this.predictFlow.inputCurveData;
+ return { powerCurve, flowCurve };
+ }
+
+ getCurrentCurves() {
+ const powerCurve = this.predictPower.currentFxyCurve[this.predictPower.currentF];
+ const flowCurve = this.predictFlow.currentFxyCurve[this.predictFlow.currentF];
+
+ return { powerCurve, flowCurve };
+
+ }
+
+ calcDistanceBEP(efficiency,maxEfficiency,minEfficiency) {
+
+ const absDistFromPeak = this.calcDistanceFromPeak(efficiency,maxEfficiency);
+ const relDistFromPeak = this.calcRelativeDistanceFromPeak(efficiency,maxEfficiency,minEfficiency);
+
+ //store internally
+ this.absDistFromPeak = absDistFromPeak ;
+ this.relDistFromPeak = relDistFromPeak;
+
+ return { absDistFromPeak: absDistFromPeak, relDistFromPeak: relDistFromPeak };
+ }
+
+ getOutput() {
+
+ // Improved output object generation
+ const output = {};
+ //build the output object
+ this.measurements.getTypes().forEach(type => {
+ this.measurements.getVariants(type).forEach(variant => {
+
+ const downstreamVal = this.measurements.type(type).variant(variant).position("downstream").getCurrentValue();
+ const upstreamVal = this.measurements.type(type).variant(variant).position("upstream").getCurrentValue();
+
+ if (downstreamVal != null) {
+ output[`downstream_${variant}_${type}`] = downstreamVal;
+ }
+ if (upstreamVal != null) {
+ output[`upstream_${variant}_${type}`] = upstreamVal;
+ }
+ if (downstreamVal != null && upstreamVal != null) {
+ const diffVal = this.measurements.type(type).variant(variant).difference().value;
+ output[`differential_${variant}_${type}`] = diffVal;
+ }
+ });
+ });
+
+ //fill in the rest of the output object
+ output["state"] = this.state.getCurrentState();
+ output["runtime"] = this.state.getRunTimeHours();
+ output["ctrl"] = this.state.getCurrentPosition();
+ output["moveTimeleft"] = this.state.getMoveTimeLeft();
+ output["mode"] = this.currentMode;
+ output["cog"] = this.cog; // flow / power efficiency
+ output["NCog"] = this.NCog; // normalized cog
+ output["NCogPercent"] = Math.round(this.NCog * 100 * 100) / 100 ;
+
+ if(this.flowDrift != null){
+ const flowDrift = this.flowDrift;
+ output["flowNrmse"] = flowDrift.nrmse;
+ output["flowLongterNRMSD"] = flowDrift.longTermNRMSD;
+ output["flowImmediateLevel"] = flowDrift.immediateLevel;
+ output["flowLongTermLevel"] = flowDrift.longTermLevel;
+ }
+
+ //should this all go in the container of measurements?
+ output["effDistFromPeak"] = this.absDistFromPeak;
+ output["effRelDistFromPeak"] = this.relDistFromPeak;
+ //this.logger.debug(`Output: ${JSON.stringify(output)}`);
+
+ return output;
+ }
+
+
+} // end of class
+
+module.exports = Machine;
+
+/*------------------- Testing -------------------*/
+
+/*
+curve = require('C:/Users/zn375/.node-red/public/fallbackData.json');
+
+//import a child
+const Child = require('../../../measurement/dependencies/measurement/measurement');
+
+console.log(`Creating child...`);
+const PT1 = new Child(config={
+ general:{
+ name:"PT1",
+ logging:{
+ enabled:true,
+ logLevel:"debug",
+ },
+ },
+ functionality:{
+ softwareType:"measurement",
+ },
+ asset:{
+ type:"sensor",
+ subType:"pressure",
+ },
+});
+
+const PT2 = new Child(config={
+ general:{
+ name:"PT2",
+ logging:{
+ enabled:true,
+ logLevel:"debug",
+ },
+ },
+ functionality:{
+ softwareType:"measurement",
+ },
+ asset:{
+ type:"sensor",
+ subType:"pressure",
+ },
+});
+
+//create a machine
+console.log(`Creating machine...`);
+
+const machineConfig = {
+ general: {
+ name: "Hydrostal",
+ logging: {
+ enabled: true,
+ logLevel: "debug",
+ }
+ },
+ asset: {
+ supplier: "Hydrostal",
+ type: "pump",
+ subType: "centrifugal",
+ model: "H05K-S03R+HGM1X-X280KO", // Ensure this field is present.
+ machineCurve: curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"],
+ }
+}
+
+const stateConfig = {
+ general: {
+ logging: {
+ enabled: true,
+ logLevel: "debug",
+ },
+ },
+ // Your custom config here (or leave empty for defaults)
+ movement: {
+ speed: 1,
+ },
+ time: {
+ starting: 2,
+ warmingup: 3,
+ stopping: 2,
+ coolingdown: 3,
+ },
+};
+
+const machine = new Machine(machineConfig, stateConfig);
+
+//machine.logger.info(JSON.stringify(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]));
+machine.logger.info(`Registering child...`);
+machine.childRegistrationUtils.registerChild(PT1, "upstream");
+machine.childRegistrationUtils.registerChild(PT2, "downstream");
+
+//feed curve to the machine class
+//machine.updateCurve(curve["machineCurves"]["Hydrostal"]["H05K-S03R+HGM1X-X280KO"]);
+
+PT1.logger.info(`Enable sim...`);
+PT1.toggleSimulation();
+PT2.logger.info(`Enable sim...`);
+PT2.toggleSimulation();
+machine.getOutput();
+//manual test
+//machine.handleInput("parent", "execSequence", "startup");
+
+machine.measurements.type("pressure").variant("measured").position('upstream').value(-200);
+machine.measurements.type("pressure").variant("measured").position('downstream').value(1000);
+
+testingSequences();
+
+const tickLoop = setInterval(changeInput,1000);
+
+function changeInput(){
+ PT1.logger.info(`tick...`);
+ PT1.tick();
+ PT2.tick();
+}
+
+async function testingSequences(){
+ try{
+ console.log(` ********** Testing sequence startup... **********`);
+ await machine.handleInput("parent", "execSequence", "startup");
+ console.log(` ********** Testing movement to 15... **********`);
+ await machine.handleInput("parent", "execMovement", 15);
+ machine.getOutput();
+ console.log(` ********** Testing sequence shutdown... **********`);
+ await machine.handleInput("parent", "execSequence", "shutdown");
+ console.log(`********** Testing moving to setpoint 10... while in idle **********`);
+ await machine.handleInput("parent", "execMovement", 10);
+ console.log(` ********** Testing sequence emergencyStop... **********`);
+ await machine.handleInput("parent", "execSequence", "emergencystop");
+ console.log(`********** Testing sequence boot... **********`);
+ await machine.handleInput("parent", "execSequence", "boot");
+ }catch(error){
+ console.error(`Error: ${error}`);
+ }
+}
+
+
+//*/
+
+
+
diff --git a/dependencies/machine/machine.test.js b/dependencies/machine/machine.test.js
new file mode 100644
index 0000000..6d60b03
--- /dev/null
+++ b/dependencies/machine/machine.test.js
@@ -0,0 +1,297 @@
+const Machine = require('./machine');
+const specs = require('../../../generalFunctions/datasets/assetData/pumps/hydrostal/centrifugal pumps/models.json');
+
+class MachineTester {
+ constructor() {
+ this.totalTests = 0;
+ this.passedTests = 0;
+ this.failedTests = 0;
+ this.machineCurve = specs[0].machineCurve;
+ }
+
+ assert(condition, message) {
+ this.totalTests++;
+ if (condition) {
+ console.log(`✓ PASS: ${message}`);
+ this.passedTests++;
+ } else {
+ console.log(`✗ FAIL: ${message}`);
+ this.failedTests++;
+ }
+ }
+
+ createBaseMachineConfig(name) {
+ return {
+ general: {
+ logging: { enabled: true, logLevel: "debug" },
+ name: name,
+ unit: "m3/h"
+ },
+ functionality: {
+ softwareType: "machine",
+ role: "RotationalDeviceController"
+ },
+ asset: {
+ type: "pump",
+ subType: "Centrifugal",
+ model: "TestModel",
+ supplier: "Hydrostal",
+ machineCurve: this.machineCurve
+ },
+ mode: {
+ current: "auto",
+ allowedActions: {
+ auto: ["execSequence", "execMovement", "statusCheck"],
+ virtualControl: ["execMovement", "statusCheck"],
+ fysicalControl: ["statusCheck"]
+ },
+ allowedSources: {
+ auto: ["parent", "GUI"],
+ virtualControl: ["GUI"],
+ fysicalControl: ["fysical"]
+ }
+ },
+ sequences: {
+ startup: ["starting", "warmingup", "operational"],
+ shutdown: ["stopping", "coolingdown", "idle"],
+ emergencystop: ["emergencystop", "off"],
+ boot: ["idle", "starting", "warmingup", "operational"]
+ },
+ calculationMode: "medium"
+ };
+ }
+
+ async testBasicOperations() {
+ console.log('\nTesting Basic Machine Operations...');
+
+ const machine = new Machine(this.createBaseMachineConfig("TestMachine"));
+
+ try {
+ // Test 1: Initialization
+ this.assert(
+ machine.currentMode === "auto",
+ 'Machine should initialize in auto mode'
+ );
+
+ // Test 2: Set pressure measurement
+ machine.measurements.type("pressure").variant("measured").position("downstream").value(800);
+ const pressure = machine.handleMeasuredPressure();
+ this.assert(
+ pressure === 800,
+ 'Should correctly handle pressure measurement'
+ );
+
+ // Test 3: State transition
+ await machine.state.transitionToState("idle");
+ this.assert(
+ machine.state.getCurrentState() === "idle",
+ 'Should transition to idle state'
+ );
+
+ // Test 4: Mode change
+ machine.setMode("virtualControl");
+ this.assert(
+ machine.currentMode === "virtualControl",
+ 'Should change mode to virtual control'
+ );
+ } catch (error) {
+ console.error('Test failed with error:', error);
+ this.failedTests++;
+ }
+ }
+
+ async testPredictions() {
+ console.log('\nTesting Machine Predictions...');
+
+ const machine = new Machine(this.createBaseMachineConfig("TestMachine"));
+ machine.measurements.type("pressure").variant("measured").position("downstream").value(800);
+
+ try {
+ // Test 1: Flow prediction
+ const flow = machine.calcFlow(50);
+ this.assert(
+ flow > 0 && !isNaN(flow),
+ 'Should calculate valid flow for control value'
+ );
+
+ // Test 2: Power prediction
+ const power = machine.calcPower(50);
+ this.assert(
+ power > 0 && !isNaN(power),
+ 'Should calculate valid power for control value'
+ );
+
+ // Test 3: Control prediction
+ const ctrl = machine.calcCtrl(100);
+ this.assert(
+ ctrl >= 0 && ctrl <= 100,
+ 'Should calculate valid control value for desired flow'
+ );
+ } catch (error) {
+ console.error('Test failed with error:', error);
+ this.failedTests++;
+ }
+ }
+
+ async testSequenceExecution() {
+ console.log('\nTesting Machine Sequences...');
+
+ const machine = new Machine(this.createBaseMachineConfig("TestMachine"));
+
+ try {
+ // Test 1: Startup sequence
+ await machine.handleInput("parent", "execSequence", "startup");
+ this.assert(
+ machine.state.getCurrentState() === "operational",
+ 'Should complete startup sequence'
+ );
+
+ // Test 2: Movement after startup
+ await machine.handleInput("parent", "execMovement", 50);
+ this.assert(
+ machine.state.getCurrentPosition() === 50,
+ 'Should move to specified position'
+ );
+
+ // Test 3: Shutdown sequence
+ await machine.handleInput("parent", "execSequence", "shutdown");
+ this.assert(
+ machine.state.getCurrentState() === "idle",
+ 'Should complete shutdown sequence'
+ );
+
+ // Test 4: Emergency stop
+ await machine.handleInput("parent", "execSequence", "emergencystop");
+ this.assert(
+ machine.state.getCurrentState() === "off",
+ 'Should execute emergency stop'
+ );
+ } catch (error) {
+ console.error('Test failed with error:', error);
+ this.failedTests++;
+ }
+ }
+
+ async testMeasurementHandling() {
+ console.log('\nTesting Measurement Handling...');
+
+ const machine = new Machine(this.createBaseMachineConfig("TestMachine"));
+
+ try {
+ // Test 1: Pressure measurement
+ machine.measurements.type("pressure").variant("measured").position("downstream").value(800);
+ machine.measurements.type("pressure").variant("measured").setUpstream(1000);
+ const pressure = machine.handleMeasuredPressure();
+ this.assert(
+ pressure === 200,
+ 'Should calculate correct differential pressure'
+ );
+
+ // Test 2: Flow measurement
+ machine.measurements.type("flow").variant("measured").position("downstream").value(100);
+ const flow = machine.handleMeasuredFlow();
+ this.assert(
+ flow === 100,
+ 'Should handle flow measurement correctly'
+ );
+
+ // Test 3: Power measurement
+ machine.measurements.type("power").variant("measured").setUpstream(75);
+ const power = machine.handleMeasuredPower();
+ this.assert(
+ power === 75,
+ 'Should handle power measurement correctly'
+ );
+
+ // Test 4: Efficiency calculation
+ machine.calcEfficiency();
+ const efficiency = machine.measurements.type("efficiency").variant("measured").getDownstream();
+ this.assert(
+ efficiency > 0 && !isNaN(efficiency),
+ 'Should calculate valid efficiency'
+ );
+ } catch (error) {
+ console.error('Test failed with error:', error);
+ this.failedTests++;
+ }
+ }
+
+ async testCurveHandling() {
+ console.log('\nTesting Machine Curve Handling...');
+
+ const machine = new Machine(this.createBaseMachineConfig("TestMachine"));
+
+ try {
+ // Test 1: Curve initialization
+ const curves = machine.showCurve();
+ this.assert(
+ curves.powerCurve && curves.flowCurve,
+ 'Should properly initialize power and flow curves'
+ );
+
+ // Test 2: Test reverse curve creation
+ const reversedCurve = machine.reverseCurve(this.machineCurve.nq);
+ this.assert(
+ reversedCurve["1"].x[0] === this.machineCurve.nq["1"].y[0] &&
+ reversedCurve["1"].y[0] === this.machineCurve.nq["1"].x[0],
+ 'Should correctly reverse x and y values in curve'
+ );
+
+ // Test 3: Update curve dynamically
+ const newCurve = {
+ nq: {
+ "1": {
+ x: [0, 25, 50, 75, 100],
+ y: [0, 125, 250, 375, 500]
+ }
+ },
+ np: {
+ "1": {
+ x: [0, 25, 50, 75, 100],
+ y: [0, 75, 150, 225, 300]
+ }
+ }
+ };
+ machine.updateCurve(newCurve);
+ const updatedCurves = machine.showCurve();
+ this.assert(
+ updatedCurves.flowCurve["1"].y[2] === 250,
+ 'Should update curve with new values'
+ );
+
+ // Test 4: Verify curve interpolation
+ machine.measurements.type("pressure").variant("measured").position("downstream").value(800);
+ const midpointCtrl = machine.calcCtrl(250); // Should interpolate between points
+ const calculatedFlow = machine.calcFlow(midpointCtrl);
+ this.assert(
+ Math.abs(calculatedFlow - 250) < 1, // Allow small numerical error
+ 'Should accurately interpolate between curve points'
+ );
+
+ } catch (error) {
+ console.error('Test failed with error:', error);
+ this.failedTests++;
+ }
+ }
+
+ async runAllTests() {
+ console.log('Starting Machine Tests...\n');
+
+ await this.testBasicOperations();
+ await this.testPredictions();
+ await this.testSequenceExecution();
+ await this.testMeasurementHandling();
+ await this.testCurveHandling();
+
+ console.log('\nTest Summary:');
+ console.log(`Total Tests: ${this.totalTests}`);
+ console.log(`Passed: ${this.passedTests}`);
+ console.log(`Failed: ${this.failedTests}`);
+
+ process.exit(this.failedTests > 0 ? 1 : 0);
+ }
+}
+
+// Run the tests
+const tester = new MachineTester();
+tester.runAllTests().catch(console.error);
\ No newline at end of file
diff --git a/dependencies/rotatingMachine/rotatingMachineConfig.json b/dependencies/rotatingMachine/rotatingMachineConfig.json
new file mode 100644
index 0000000..dfc366b
--- /dev/null
+++ b/dependencies/rotatingMachine/rotatingMachineConfig.json
@@ -0,0 +1,381 @@
+{
+ "general": {
+ "name": {
+ "default": "Rotating Machine",
+ "rules": {
+ "type": "string",
+ "description": "A human-readable name or label for this machine configuration."
+ }
+ },
+ "id": {
+ "default": null,
+ "rules": {
+ "type": "string",
+ "nullable": true,
+ "description": "A unique identifier for this configuration. If not provided, defaults to null."
+ }
+ },
+ "unit": {
+ "default": "m3/h",
+ "rules": {
+ "type": "string",
+ "description": "The default measurement unit for this configuration (e.g., 'meters', 'seconds', 'unitless')."
+ }
+ },
+ "logging": {
+ "logLevel": {
+ "default": "info",
+ "rules": {
+ "type": "enum",
+ "values": [
+ {
+ "value": "debug",
+ "description": "Log messages are printed for debugging purposes."
+ },
+ {
+ "value": "info",
+ "description": "Informational messages are printed."
+ },
+ {
+ "value": "warn",
+ "description": "Warning messages are printed."
+ },
+ {
+ "value": "error",
+ "description": "Error messages are printed."
+ }
+ ]
+ }
+ },
+ "enabled": {
+ "default": true,
+ "rules": {
+ "type": "boolean",
+ "description": "Indicates whether logging is active. If true, log messages will be generated."
+ }
+ }
+ }
+ },
+ "functionality": {
+ "softwareType": {
+ "default": "machine",
+ "rules": {
+ "type": "string",
+ "description": "Specified software type for this configuration."
+ }
+ },
+ "role": {
+ "default": "RotationalDeviceController",
+ "rules": {
+ "type": "string",
+ "description": "Indicates the role this configuration plays within the system."
+ }
+ }
+ },
+ "asset": {
+ "uuid": {
+ "default": null,
+ "rules": {
+ "type": "string",
+ "nullable": true,
+ "description": "A universally unique identifier for this asset. May be null if not assigned."
+ }
+ },
+ "geoLocation": {
+ "default": {},
+ "rules": {
+ "type": "object",
+ "description": "An object representing the asset's physical coordinates or location.",
+ "schema": {
+ "x": {
+ "default": 0,
+ "rules": {
+ "type": "number",
+ "description": "X coordinate of the asset's location."
+ }
+ },
+ "y": {
+ "default": 0,
+ "rules": {
+ "type": "number",
+ "description": "Y coordinate of the asset's location."
+ }
+ },
+ "z": {
+ "default": 0,
+ "rules": {
+ "type": "number",
+ "description": "Z coordinate of the asset's location."
+ }
+ }
+ }
+ }
+ },
+ "supplier": {
+ "default": "Unknown",
+ "rules": {
+ "type": "string",
+ "description": "The supplier or manufacturer of the asset."
+ }
+ },
+ "type": {
+ "default": "pump",
+ "rules": {
+ "type": "string",
+ "description": "A general classification of the asset tied to the specific software. This is not chosen from the asset dropdown menu."
+ }
+ },
+ "subType": {
+ "default": "Centrifugal",
+ "rules": {
+ "type": "string",
+ "description": "A more specific classification within 'type'. For example, 'centrifugal' for a centrifugal pump."
+ }
+ },
+ "model": {
+ "default": "Unknown",
+ "rules": {
+ "type": "string",
+ "description": "A user-defined or manufacturer-defined model identifier for the asset."
+ }
+ },
+ "accuracy": {
+ "default": null,
+ "rules": {
+ "type": "number",
+ "nullable": true,
+ "description": "The accuracy of the machine or sensor, typically as a percentage or absolute value."
+ }
+ },
+ "machineCurve": {
+ "default": {
+ "nq": {
+ "1": {
+ "x": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ],
+ "y": [
+ 10,
+ 20,
+ 30,
+ 40,
+ 50
+ ]
+ }
+ },
+ "np": {
+ "1": {
+ "x": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ],
+ "y": [
+ 10,
+ 20,
+ 30,
+ 40,
+ 50
+ ]
+ }
+ }
+ },
+ "rules": {
+ "type": "machineCurve",
+ "description": "All machine curves must have a 'nq' and 'np' curve. nq stands for the flow curve, np stands for the power curve. Together they form the efficiency curve."
+ }
+ }
+ },
+ "mode": {
+ "current": {
+ "default": "auto",
+ "rules": {
+ "type": "enum",
+ "values": [
+ {
+ "value": "auto",
+ "description": "Machine accepts setpoints from a parent controller and runs autonomously."
+ },
+ {
+ "value": "virtualControl",
+ "description": "Controlled via GUI setpoints; ignores parent commands."
+ },
+ {
+ "value": "fysicalControl",
+ "description": "Controlled via physical buttons or switches; ignores external automated commands."
+ },
+ {
+ "value": "maintenance",
+ "description": "No active control from auto, virtual, or fysical sources."
+ }
+ ],
+ "description": "The operational mode of the machine."
+ }
+ },
+ "allowedActions":{
+ "default":{},
+ "rules": {
+ "type": "object",
+ "schema":{
+ "auto": {
+ "default": ["statusCheck", "execMovement", "execSequence", "emergencyStop"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Actions allowed in auto mode."
+ }
+ },
+ "virtualControl": {
+ "default": ["statusCheck", "execMovement", "execSequence", "emergencyStop"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Actions allowed in virtualControl mode."
+ }
+ },
+ "fysicalControl": {
+ "default": ["statusCheck", "emergencyStop"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Actions allowed in fysicalControl mode."
+ }
+ },
+ "maintenance": {
+ "default": ["statusCheck"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Actions allowed in maintenance mode."
+ }
+ }
+ },
+ "description": "Information about valid command sources recognized by the machine."
+ }
+ },
+ "allowedSources":{
+ "default": {},
+ "rules": {
+ "type": "object",
+ "schema":{
+ "auto": {
+ "default": ["parent", "GUI", "fysical"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Sources allowed in auto mode."
+ }
+ },
+ "virtualControl": {
+ "default": ["GUI", "fysical"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Sources allowed in virtualControl mode."
+ }
+ },
+ "fysicalControl": {
+ "default": ["fysical"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Sources allowed in fysicalControl mode."
+ }
+ }
+ },
+ "description": "Information about valid command sources recognized by the machine."
+ }
+ }
+ },
+ "source": {
+ "default": "parent",
+ "rules": {
+ "type": "enum",
+ "values": [
+ {
+ "value": "parent",
+ "description": "Commands are received from a parent controller."
+ },
+ {
+ "value": "GUI",
+ "description": "Commands are received from a graphical user interface."
+ },
+ {
+ "value": "fysical",
+ "description": "Commands are received from physical buttons or switches."
+ }
+ ],
+ "description": "Information about valid command sources recognized by the machine."
+ }
+ },
+ "sequences":{
+ "default":{},
+ "rules": {
+ "type": "object",
+ "schema": {
+ "startup": {
+ "default": ["starting","warmingup","operational"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Sequence of states for starting up the machine."
+ }
+ },
+ "shutdown": {
+ "default": ["stopping","coolingdown","idle"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Sequence of states for shutting down the machine."
+ }
+ },
+ "emergencystop": {
+ "default": ["emergencystop","off"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Sequence of states for an emergency stop."
+ }
+ },
+ "boot": {
+ "default": ["idle","starting","warmingup","operational"],
+ "rules": {
+ "type": "set",
+ "itemType": "string",
+ "description": "Sequence of states for booting up the machine."
+ }
+ }
+ }
+ },
+ "description": "Predefined sequences of states for the machine."
+
+ },
+ "calculationMode": {
+ "default": "medium",
+ "rules": {
+ "type": "enum",
+ "values": [
+ {
+ "value": "low",
+ "description": "Calculations run at fixed intervals (time-based)."
+ },
+ {
+ "value": "medium",
+ "description": "Calculations run when new setpoints arrive or measured changes occur (event-driven)."
+ },
+ {
+ "value": "high",
+ "description": "Calculations run on all event-driven info, including every movement."
+ }
+ ],
+ "description": "The frequency at which calculations are performed."
+ }
+ }
+ }
+
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d38a86b
--- /dev/null
+++ b/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "rotatingMachine",
+ "version": "1.0.3",
+ "description": "Control module rotatingMachine",
+ "main": "rotatingMachine.js",
+ "scripts": {
+ "test": "node rotatingMachine.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://gitea.centraal.wbd-rd.nl/RnD/rotatingMachine.git"
+ },
+ "keywords": [
+ "rotatingMachine",
+ "node-red"
+ ],
+ "author": "Rene De Ren",
+ "license": "SEE LICENSE",
+ "dependencies": {
+ "generalFunctions": "git+https://gitea.centraal.wbd-rd.nl/RnD/generalFunctions.git",
+ "convert": "git+https://gitea.centraal.wbd-rd.nl/RnD/convert.git"
+ },
+ "node-red": {
+ "nodes": {
+ "rotatingMachine": "rotatingMachine.js"
+ }
+ }
+}
diff --git a/readme/Training_data b/readme/Training_data
new file mode 100644
index 0000000..382a413
--- /dev/null
+++ b/readme/Training_data
@@ -0,0 +1,22 @@
+let ctrl0 = [0,0.4,1.4,2.4,3.4,4.4,5.4,6.4,7.4,8.4,9.4,10.4,11.6,12.6,13.8,14.8,15.8,16.8,17.8,18.8,19.8,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,51.5,52,53,54,55,56,57,58.2,59.2,60.2,61.2,62.2,63.2,64.2,65.2,66.2,67.2,68.2,69.2,70.2,71.2,72.2,73.2,74.2,75.2,76.2,77.4,78.6,79.8,80.8,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.4,67.4,65.4,63.4,61.4,59.4,57.4,55.4,53.4,51.4,49.4,48.3,47.2,45.2,43.2,41,39,37,35,33,31,29,27,25,23,21,19,17,15,13,11,9,7,5,3,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30.2,31.4,32.4,33.4,34.4,35.4,36.4,37.4,38.4,39.4,40.4,41.4,42.4,43.6,44.2,44.8,45.8,46.8,47.8,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,84.8,82.8,80.8,78.8,76.6,74.6,72.6,70.6,68.6,66.6,64.6,62.4,60.4,58.4,56.4,54.4,53.3,52.2,50.2,48,46,44,41.8,39.8,37.8,35.8,33.6,31.6,29.6,27.6,25.6,23.6,21.6,19.6,17.6,15.6,13.6,11.6,9.6,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.6,5.8,6.8,7.8,8.8,9.8,10.8,11.8,12.8,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32.5,33,34,35,36.2,37.4,38.4,39.4,40.4,41.4,42.6,43.6,44.6,45.6,46.6,47.6,48.8,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,67.5,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,85,83,81,78.8,76.8,74.8,72.8,70.8,68.8,66.8,64.8,62.8,60.8,58.8,56.8,54.8,52.8,50.8,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.6,24.6,22.6,20.6,18.6,16.6,14.6,12.6,10.6,8.6,6.6,4.6,2.6,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21.2,22.4,23.4,24.4,25.4,26.4,27.4,28.4,29.4,30.4,31.4,32.4,33.6,34.2,34.8,35.8,36.8,37.8,38.8,39.8,40.8,41.8,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.2,67.2,65.2,63.2,61.2,59.2,57.2,55,53,51,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.8,24.8,22.8,20.8,18.8,16.8,14.8,12.8,10.8,8.6,6.6,4.6,2.6,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,33.6,31.6,29.6,27.6,25.6,23.6,21.6,19.6,17.6,15.6,13.6,11.6,9.6,7.6,5.6,3.6,2.5,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.6,79.6,80.6,81.6,82.2,82.8,83.8,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99];
+let ctrl1 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30.2,31.4,32.4,33.4,34.4,35.4,36.4,37.4,38.4,39.4,40.4,41.4,42.4,43.6,44.2,44.8,45.8,46.8,47.8,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,84.8,82.8,80.8,78.8,76.6,74.6,72.6,70.6,68.6,66.6,64.6,62.4,60.4,58.4,56.4,54.4,53.3,52.2,50.2,48,46,44,41.8,39.8,37.8,35.8,33.6,31.6,29.6,27.6,25.6,23.6,21.6,19.6,17.6,15.6,13.6,11.6,9.6,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.6,5.8,6.8,7.8,8.8,9.8,10.8,11.8,12.8,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32.5,33,34,35,36.2,37.4,38.4,39.4,40.4,41.4,42.6,43.6,44.6,45.6,46.6,47.6,48.8,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,67.5,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,85,83,81,78.8,76.8,74.8,72.8,70.8,68.8,66.8,64.8,62.8,60.8,58.8,56.8,54.8,52.8,50.8,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.6,24.6,22.6,20.6,18.6,16.6,14.6,12.6,10.6,8.6,6.6,4.6,2.6,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21.2,22.4,23.4,24.6,25.6,26.6,27.2,27.8,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.2,67.2,65.2,63.2,61.2,59.2,57.2,55,53,51,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.8,24.8,22.8,20.8,18.8,16.8,14.8,12.8,10.8,8.6,6.6,4.6,2.6,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,33.6,31.6,29.6,27.6,25.6,23.6,21.6,19.6,17.6,15.6,13.6,12.5,11.4,9.4,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.6,79.6,80.6,81.6,82.2,82.8,83.8,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99];
+let ctrl2 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.6,1.6,2.6,3.6,4.6,5.8,6.8,7.8,8.8,9.8,10.8,11.8,12.8,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32.5,33,34,35,36.2,37.4,38.4,39.4,40.4,41.4,42.6,43.6,44.6,45.6,46.6,47.6,48.8,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,67.5,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97,95,93,91,89,87,85,83,81,78.8,76.8,74.8,72.8,70.8,68.8,66.8,64.8,62.8,60.8,58.8,56.8,54.8,52.8,50.8,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.6,24.6,22.6,20.6,18.6,16.6,14.6,12.6,10.6,8.6,6.6,4.6,2.6,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21.2,22.4,23.4,24.6,25.6,26.6,27.2,27.8,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.2,67.2,65.2,63.2,61.2,59.2,57.2,55,53,51,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.8,24.8,22.8,20.8,18.8,16.8,14.8,12.8,10.8,8.6,6.6,4.6,2.6,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,34.5,33.4,31.4,29.4,27.4,25.4,23.4,21.4,19.4,17.4,15.4,13.4,11.4,9.4,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.6,79.6,80.6,81.6,82.2,82.8,83.8,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99];
+let ctrl3 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21.2,22.4,23.4,24.6,25.6,26.6,27.2,27.8,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,97.4,95.4,93.4,91.4,89.4,87.4,85.4,83.4,81.4,79.4,77.4,75.4,73.4,71.4,69.2,67.2,65.2,63.2,61.2,59.2,57.2,55,53,51,48.8,46.8,44.8,42.8,40.8,38.8,36.8,34.8,32.8,30.8,28.8,26.8,24.8,22.8,20.8,18.8,16.8,14.8,12.8,10.8,8.6,6.6,4.6,2.6,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,34.5,33.4,31.4,29.4,27.4,25.4,23.4,21.4,19.4,17.4,15.4,13.4,11.4,9.4,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.6,79.6,80.6,81.6,82.2,82.8,83.8,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99];
+let ctrl4 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,6.5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23.2,24.2,25.4,26.6,27.6,28.6,29.6,30.6,31.6,32.6,33.6,34.8,35.8,36.8,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,64.5,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80.2,81.2,82.2,83.4,84.4,85.4,86.4,87.4,88.4,89.4,90.6,91.6,92.8,94,95,96,97,98,99,98,96,94,92,90,88,86,84,82,79.8,77.8,75.8,73.8,71.8,69.8,67.8,65.8,63.8,61.8,59.8,57.8,55.8,53.8,51.8,49.8,47.8,45.8,43.8,41.8,39.8,37.8,35.6,34.5,33.4,31.4,29.4,27.4,25.4,23.4,21.4,19.4,17.4,15.4,13.4,11.4,9.4,7.4,5.4,3.4,1.4,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.4,79.4,80.4,81.4,82.6,83.6,84.6,85.6,86.6,87.6,88.6,89.6,90.6,92,92.6,93.6,94.6,95.6,96.2,96.8,97.8,98.8];
+let ctrl5 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.6,1.6,2.6,3.6,4.8,5.8,6.8,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66.2,67.2,68.2,69.2,70.2,71.4,72.4,73.4,74.4,75.4,76.4,77.4,78.4,79.4,80.4,81.4,82.6,83.6,84.6,85.6,86.6,87.6,88.6,89.6,90.6,92,92.6,93.6,94.6,95.6,96.2,96.8,97.8,98.8];
+
+let PT_1 = [85.9,87.4,90.5,93.4,96.8,99.9,103,107,110,114,118,121,125,129,133,137,140,143,147,150,153,155,157,159,161,162,163,165,166,168,169,170,171,172,174,174,176,177,178,179,181,181,182,184,184,185,186,187,188,189,190,191,193,194,195,196,196,197,199,199,201,201,202,203,204,206,206,207,208,210,210,211,213,214,215,215,217,218,219,219,219,219,219,219,219,219,219,219,219,219,219,219,218,219,218,218,218,218,219,219,219,219,218,219,219,218,218,218,218,219,220,218,216,215,212,210,208,206,204,202,200,198,197,194,193,190,189,186,184,182,180,178,175,173,171,168,166,163,159,156,152,147,142,134,127,119,112,104,97.2,92.1,92.2,95.2,98.6,102,105,109,112,116,119,123,127,131,135,138,142,144,147,150,152,154,156,159,160,162,164,165,167,168,169,170,172,173,174,175,177,178,179,180,181,182,183,184,185,184,185,186,187,188,189,190,191,192,193,195,195,197,198,198,199,201,202,203,204,205,206,207,208,208,210,211,212,212,213,215,216,217,217,217,217,218,218,217,217,218,217,217,217,217,217,217,217,217,217,217,218,217,217,217,217,218,218,217,217,217,217,217,218,217,217,216,215,212,210,209,206,204,202,200,198,197,194,191,190,187,186,184,182,179,178,175,173,170,168,165,163,160,156,152,148,143,137,130,123,115,107,101,93.4,90.2,92.1,95.1,98.7,102,106,109,113,116,121,124,129,132,135,139,142,144,147,150,152,154,156,157,159,161,163,164,166,167,169,170,171,172,173,175,176,177,179,180,181,181,182,184,184,185,186,187,188,189,190,191,192,193,194,195,196,197,199,200,200,202,203,204,205,206,207,208,209,210,209,210,211,212,214,215,216,216,217,217,216,217,217,217,217,216,217,217,217,217,217,217,217,217,218,217,217,218,218,217,218,217,217,217,217,217,217,217,217,217,217,215,213,211,209,207,204,203,201,198,196,194,192,190,189,187,185,183,181,179,177,175,172,170,167,165,162,158,155,150,145,140,132,126,118,110,102,96,90.3,90.9,93.8,96.4,100,103,107,111,115,118,122,125,130,133,136,138,141,144,146,148,151,153,155,157,158,160,161,163,165,166,168,169,170,171,173,173,175,176,178,178,179,181,181,183,183,184,185,186,187,187,188,188,190,190,192,192,194,194,195,196,197,198,200,201,202,203,204,205,206,208,209,209,210,211,213,214,211,207,206,206,205,206,206,206,206,206,206,206,205,206,205,206,206,205,205,205,205,205,206,206,205,206,205,206,205,205,206,205,206,205,204,202,200,197,195,193,191,189,186,184,182,180,178,175,174,172,171,172,177,175,173,171,169,167,164,161,157,154,150,146,141,134,128,121,113,106,99.4,93,90,92.4,95.7,98.7,102,105,109,112,116,120,124,128,131,134,136,139,141,143,145,147,149,151,153,155,156,158,160,161,162,164,166,166,168,169,171,172,173,174,175,177,177,177,178,179,180,180,181,182,184,184,185,186,187,189,189,190,191,191,193,194,195,194,188,187,189,190,191,192,193,195,196,197,198,200,200,202,203,204,205,206,205,205,205,205,206,205,205,205,205,205,205,205,205,206,205,205,205,205,205,206,206,205,204,205,205,204,205,205,204,203,201,198,195,193,191,189,186,184,182,178,176,175,173,170,169,167,164,163,160,159,155,156,161,159,156,154,150,147,143,138,133,128,121,115,108,102,95.3,89.5,89.8,92.3,95.5,98.9,102,106,110,114,117,120,124,127,130,133,135,136,138,139,140,142,144,146,148,150,151,153,154,155,157,159,160,162,163,164,166,167,167,168,169,170,171,173,174,175,175,177,178,178,179,180,181,181,183,178,173,176,177,177,179,181,182,183,184,186,186,188,189,190,191,192,193,195,196,197,198,199,201,201,202,203,203,203,203,202,203,204,204,203,204,203,204,203,203,203,204,204,204,154];
+let PT_2 = [47.9,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.7,47.6,47.6,47.5,47.5,47.4,47.3,47.2,47.2,47,46.9,46.8,46.5,46.4,46.2,46.1,45.9,45.6,45.5,45.4,45.3,45.1,44.9,44.6,44.6,44.3,44,43.8,43.5,43.7,43.4,43.2,43,42.7,42.4,42.4,42,41.9,41.6,41.6,41.2,41.3,40.7,40.6,40.4,40.3,40,39.5,39.3,39.2,38.8,38.5,38.3,37.7,37.8,37.4,37.3,36.7,36.5,36.4,35.8,35.5,35.2,35.2,34.9,35,34.8,34.9,35,35.1,35.3,34.9,35.1,34.9,35,34.8,35,34.8,34.7,34.8,34.8,34.9,34.7,35,34.9,34.9,56.9,80.8,81,81.1,81.6,81.5,81.3,81.5,81.7,81.4,81.9,82.2,82.8,83.3,84.2,84.6,85.3,85.7,86.1,86.7,87.3,87.7,88.1,88.6,88.9,89.5,89.9,90.3,90.5,91.1,91.2,91.5,92.3,92.7,93,93.4,93.8,93.9,94.1,94.2,94.5,94.7,94.8,94.8,94.9,94.9,94.9,95,96.8,99.9,103,106,110,114,117,121,124,128,132,136,140,144,148,152,153,158,161,163,165,167,169,170,172,174,176,178,178,181,182,184,185,186,186,189,190,191,192,193,195,195,196,197,199,200,201,202,203,204,206,207,208,209,209,210,212,213,214,215,216,217,218,219,220,222,223,223,225,227,227,229,230,230,232,233,234,236,236,237,237,237,238,237,237,237,237,237,237,237,237,237,237,237,236,237,236,237,236,236,236,236,236,237,236,237,236,236,237,236,234,231,229,226,224,222,220,218,215,213,211,209,207,204,202,200,198,196,194,191,189,186,184,180,177,174,171,168,164,160,155,148,141,133,125,117,110,103,99.8,102,105,109,112,116,120,123,127,132,135,140,143,147,150,154,157,160,162,164,166,168,170,171,173,175,176,178,179,181,183,184,185,186,188,189,191,192,192,194,195,197,198,198,200,201,202,203,204,205,206,207,208,210,210,212,212,214,215,216,217,218,220,221,223,222,224,225,227,227,228,228,229,230,231,233,234,235,236,236,236,237,236,236,235,237,236,236,236,237,236,236,237,236,236,235,236,236,236,236,236,237,235,236,236,236,235,235,235,234,231,229,226,224,222,220,217,215,213,210,209,207,205,203,201,199,196,195,192,190,187,184,182,179,176,172,169,165,162,157,151,143,136,127,120,112,105,100,100,104,107,110,114,118,122,125,129,134,138,141,144,147,151,153,156,159,162,164,167,169,169,170,172,173,175,176,178,180,181,182,184,186,186,188,189,191,192,194,194,195,197,197,198,200,201,201,202,202,203,205,206,207,208,209,211,211,212,214,215,216,217,218,219,220,222,222,223,225,227,227,227,230,231,227,223,224,224,224,225,225,225,224,225,225,225,225,225,224,224,225,225,224,225,225,224,225,224,224,223,224,225,225,224,224,225,224,223,221,218,216,213,211,209,206,203,201,199,197,194,192,190,188,186,183,186,190,187,185,183,180,178,175,172,169,166,161,157,151,145,138,131,122,114,107,102,98.2,100,104,107,111,114,118,122,126,130,134,138,141,144,146,148,151,152,154,157,160,163,165,166,168,171,174,176,179,180,181,178,180,182,183,184,186,187,188,189,190,190,191,192,193,194,196,197,197,199,199,201,202,203,204,205,205,207,208,209,209,208,203,202,204,205,206,207,208,210,211,213,213,214,216,217,219,219,221,222,222,223,224,224,223,224,224,224,224,223,223,222,222,223,222,223,223,223,223,223,224,224,224,222,223,223,223,222,222,219,217,213,211,208,205,203,200,198,195,194,191,189,186,185,183,180,177,175,173,171,167,168,174,172,169,166,163,159,154,149,144,139,132,126,118,111,104,98.4,98.6,102,105,109,112,116,120,124,127,131,135,138,140,143,145,147,149,150,150,153,155,157,160,161,164,168,169,171,173,174,176,178,179,181,183,184,183,184,184,184,185,186,188,188,189,190,191,193,193,195,195,196,196,193,187,191,191,192,194,196,196,198,200,200,202,203,205,206,207,208,210,212,213,213,215,217,218,220,220,221,221,223,222,224,224,223,224,224,223,223,224,223,223,224,224,223,223,189];
+let PT_3 = [47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.7,47.7,47.7,47.7,47.7,47.7,47.6,47.6,47.5,47.5,47.4,47.3,47.2,47.1,47,46.8,46.6,46.5,46.3,46.2,46.1,45.9,45.7,45.6,45.4,45.1,45,44.9,44.7,44.4,44.2,44.2,44,43.8,43.4,43.3,43.1,42.9,42.8,42.7,42.3,42.3,42.1,41.8,41.4,41.4,40.9,40.9,40.3,40.5,40.2,39.8,39.6,39.4,39.1,38.9,38.4,38.3,38.3,37.8,37.6,37.1,36.9,36.6,36.3,36.1,35.7,35.4,35.1,35.3,35.1,35,34.8,35.1,34.8,34.8,35,34.9,34.7,34.9,35,34.9,35.1,35.2,35.4,35.1,35.2,35,35.1,35,34.9,35.1,34.8,35,34.9,34.6,34.7,34.8,35,34.8,35,34.9,35.6,36.2,36.8,37.2,37.8,38.2,38.9,39.3,40,40.7,41,41.3,41.8,42.1,42.5,42.9,43.4,43.7,44.1,44.5,44.8,45.1,45.5,45.8,46.1,46.5,46.7,47,47.3,47.4,47.6,47.7,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.8,47.7,47.7,47.7,47.7,47.7,47.6,47.1,46.8,46.3,46,45.7,45.3,44.8,44.4,43.9,43.6,43,42.6,42,41.3,40.8,40.3,39.7,39,38.4,37.7,37.2,36.4,35.8,35.2,34.5,33.9,33,32.6,32.5,31.7,31.1,30.4,29.6,29,28.2,27.3,26.7,25.9,25.1,24.1,23.5,23,22,21.3,20,19.3,18.5,17.4,16.5,15.2,14.4,13.3,12.5,12.1,10.8,9.61,8.65,7.39,6.48,5.23,4.37,3.25,3.11,3.11,2.53,3.09,2.77,2.54,2.36,2.77,2.29,2.61,2.59,2.59,2.57,2.36,2.63,2.73,2.97,3.01,2.64,2.72,2.6,3.51,49,49.1,49.7,49.1,49.5,49.7,49.2,49.4,49.1,49.7,51,52.9,55.2,56.7,59.3,61,62.7,64.7,66.4,68.4,69.9,71.3,73.1,74.3,75.8,77.6,78.6,79.9,81.2,82.8,84,85.1,86.5,87.7,88.7,89.6,90.5,91.5,92.4,93,93.6,94,94.1,94.4,94.3,94.4,94.4,94.8,97.4,100,104,107,111,114,118,121,125,129,134,138,142,146,149,153,156,159,161,164,166,168,170,172,173,175,176,178,179,181,182,183,185,186,187,189,190,191,192,193,194,195,196,197,199,200,201,202,204,204,205,206,207,208,210,210,212,213,214,215,215,217,218,219,221,221,223,224,224,224,225,226,227,227,229,230,231,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,232,231,232,231,231,232,232,233,232,232,232,232,231,232,231,229,227,224,223,221,219,215,214,212,209,208,206,203,201,200,198,196,194,191,189,186,184,181,178,176,173,169,166,161,156,149,142,134,126,118,111,105,98.8,98.9,102,106,109,113,116,120,123,127,131,135,139,143,146,149,152,155,158,160,162,164,166,168,170,171,173,175,176,178,179,180,182,183,185,186,187,188,190,190,192,193,194,196,196,197,199,200,201,201,201,202,204,205,206,206,207,209,210,211,213,214,214,216,217,218,219,221,221,223,224,224,226,226,229,229,226,222,222,222,222,221,222,222,222,222,221,222,222,223,222,221,221,222,222,221,222,222,222,222,222,221,222,221,222,222,221,221,221,222,219,217,214,212,209,208,205,202,200,198,196,194,191,190,187,185,183,186,189,187,185,183,180,178,175,172,168,165,161,156,150,143,137,128,122,114,108,101,98.1,100,104,107,111,114,118,121,125,130,133,137,140,143,146,149,151,153,155,158,160,162,164,166,167,169,170,172,174,176,177,178,180,182,182,184,185,187,187,189,190,190,191,191,193,194,195,196,197,198,199,200,202,203,203,205,205,206,207,208,210,208,204,201,205,206,207,209,210,210,213,215,215,216,217,219,219,221,222,223,222,224,222,222,223,222,223,222,222,222,223,223,222,222,222,222,223,223,222,223,222,223,223,222,222,222,223,223,221,220,217,215,213,210,208,204,202,199,197,195,192,190,188,185,184,181,179,177,174,173,169,170,176,173,170,167,164,159,154,149,144,137,131,123,117,110,104,98,98.3,101,104,108,111,115,119,123,126,130,133,136,139,141,144,147,148,150,151,153,155,158,160,163,164,167,168,169,171,173,174,176,177,178,179,181,181,182,183,184,186,186,188,188,190,191,191,193,194,195,196,197,197,192,187,191,191,194,194,196,196,199,199,200,202,203,205,207,208,209,211,211,214,214,216,218,219,220,220,222,223,225,224,226,224,224,223,224,224,223,224,224,225,224,223,224,224,171];
+let PT_4 = [104,104,105,105,105,105,105,104,104,105,104,104,105,105,104,104,104,105,104,103,104,104,104,104,104,103,104,104,103,103,103,103,102,102,102,102,102,102,102,101,101,102,100,99.3,100,101,99.9,99.4,100,99.5,98.9,99.5,99.4,98.9,97.6,98.2,98.1,97.6,96.7,97,97.5,96.2,95.8,96.3,95.9,94.8,96,95,94.2,94,94.4,94.1,93.7,93.1,92.3,91.7,93.2,92,91.3,91.9,92.3,91,91.1,93,90.4,91.2,92.4,90.6,91.5,91.4,90.1,92,92.6,90.8,91,92.9,91.7,91.1,90.5,91.6,92.4,91.2,91.4,91.8,92.2,90.9,91.7,92.8,91.7,90.5,92.4,91.5,91.3,92.6,94.7,94.4,94.3,95.2,96.5,97,96,97,98.1,97.5,98.6,99.6,98.8,100,101,101,101,100,101,103,103,102,103,103,103,103,104,105,104,104,106,105,104,105,105,104,105,105,104,105,105,104,105,106,104,104,106,105,104,103,105,104,103,103,103,102,102,101,101,101,100,99.7,98.6,99.4,98.8,97.5,95.9,96.8,96.2,95.5,94.5,92.9,92.7,93.3,92.3,90,89.3,90.1,89.7,88.3,87.5,87.4,87.3,86.7,84.9,83.4,84.4,83.5,81.7,81.7,81.1,79.1,79,79.1,77.5,76.3,76.5,74.9,73.2,73.4,71.4,69.7,70.3,69.8,67.6,66.1,66.2,65.8,63.2,62.6,60.8,61.2,59.1,58.4,60.1,59,58.3,59,59.3,59.5,58.1,58.8,58.8,59.5,58.5,58.5,59.4,58.8,58.6,59.8,59.6,58.5,57.8,59.4,58.6,58.4,59.1,60.5,58.1,58.4,58.9,58.8,58.6,59,57.6,60.9,63.2,64.3,66.5,69.8,71.2,72.8,74.1,76.8,79.2,79.1,80.7,82.8,85.6,85.7,86.6,89.6,90.9,90.8,92.3,94.3,95.5,95.5,98.3,99.3,99.6,101,103,102,103,103,105,105,104,104,105,104,104,104,104,105,106,104,105,105,104,104,105,105,104,104,105,104,102,102,103,102,101,99.7,98.5,98.7,97.9,95.4,94.8,94.1,93.4,92.3,91.1,89.9,87,87.7,86.5,84.6,82.8,82.2,80.8,79.8,78.3,76.4,76.8,75.3,73,71.6,72,68.1,66.5,67,65.2,63.1,61.3,58.9,57.7,58.1,55,53.6,52.9,50,47.6,45.6,45.2,43,40.1,40,37,34.7,32.6,31.6,29.5,27.2,24.4,22.1,21,17.9,15.3,13.5,13.6,12.9,11,11.3,12.6,13.9,12.5,11.6,13.4,12.6,12.8,13.1,13.2,12.9,11.9,13,13.5,11.6,11.9,13.3,13.2,12.1,12.6,13,14,12.4,11,12.7,13.1,13,12.5,13,15.1,18.3,21.8,26.3,31.3,36.2,38.9,42,46.8,49.7,52.6,56.5,59.3,61.3,64.8,69.4,71.7,73.8,77.5,80.7,81.5,84.2,87.4,89.8,91.8,94.1,96.9,97.8,99,102,103,104,104,104,105,104,104,103,107,112,111,121,126,132,135,137,144,149,153,156,158,154,151,153,154,155,156,156,157,157,158,160,160,161,162,162,162,163,163,163,165,165,166,166,167,168,168,169,170,170,170,172,172,173,174,175,174,175,175,176,176,177,178,178,178,179,179,179,180,181,181,182,181,182,183,184,183,185,184,185,186,185,186,186,183,178,178,178,179,180,183,182,182,182,182,181,183,182,183,182,182,182,183,181,181,182,182,182,182,182,181,181,182,182,181,180,179,176,175,175,173,172,171,170,169,168,167,166,164,164,162,160,159,157,156,159,162,160,159,156,154,152,149,151,148,146,142,143,141,137,134,132,129,124,117,110,106,109,114,118,122,127,132,136,142,146,151,156,159,162,164,165,164,155,156,157,159,161,160,161,162,163,163,163,163,165,165,166,166,167,168,168,168,170,170,170,171,170,170,171,171,176,174,174,176,177,175,176,177,178,178,179,179,179,181,179,181,178,173,169,172,172,172,174,174,174,176,176,175,177,178,179,180,179,182,182,183,185,185,186,187,187,187,187,188,188,188,188,188,187,187,188,188,188,189,188,188,187,187,187,188,187,186,184,182,180,179,177,176,173,173,172,171,170,168,166,165,163,163,160,158,154,153,151,149,147,144,145,154,151,148,147,145,145,142,140,137,134,131,129,124,119,110,102,102,105,110,113,118,123,128,133,138,142,148,151,156,159,162,163,166,162,156,157,157,157,156,158,159,161,161,163,164,165,166,166,166,167,168,169,168,168,169,170,171,171,172,172,173,174,174,174,175,176,176,177,175,171,167,170,170,171,171,171,172,172,171,172,175,175,177,177,178,177,178,179,179,181,181,181,183,183,185,183,186,184,189,186,188,190,189,191,192,192,192,193,192,192,191,191,192,176];
+let PT_5 = [46.7,46.7,46.7,46.7,46.7,46.7,46.7,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.5,46.5,46.5,46.4,46.4,46.3,46.1,46,46,45.8,45.7,45.6,45.4,45.3,45.1,44.9,44.7,44.6,44.5,44.3,44,43.9,43.8,43.6,43.2,43.2,43.1,42.8,42.6,42.4,42.1,42,41.9,41.7,41.4,41.3,40.9,40.8,40.6,40.5,39.9,40.1,39.5,39.4,39.2,39.1,38.7,38.6,38.2,38,37.9,37.5,37.2,37.1,36.7,36.4,36,35.7,35.5,34.9,35.1,34.6,34.2,33.9,33.7,33.9,33.7,33.9,33.8,34.1,34.1,33.9,33.9,33.9,33.8,33.8,33.8,34.1,33.7,34.2,33.8,33.9,34,33.8,33.8,33.7,33.7,33.7,33.9,33.8,33.7,33.7,33.8,33.7,33.8,33.7,33.9,34.5,34.7,35.5,36.1,36.7,37.3,37.7,38.2,38.9,39.2,39.9,40.3,40.4,40.9,41.4,41.8,42.3,42.6,43,43.4,43.6,43.9,44.4,44.7,44.8,45.1,45.6,45.8,46.1,46.3,46.5,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.5,46.5,46.5,46.5,46.5,46.5,46.5,46.5,46.4,46,45.6,45.2,44.8,44.5,44.1,43.8,43.3,42.8,42.3,41.9,41.3,40.8,40.1,39.9,39.2,38.6,37.9,37.2,36.5,35.9,35.3,34.7,34.1,33.3,32.6,32.2,31.3,31.5,30.6,29.9,29.2,28.4,27.9,27.2,26.4,25.5,24.9,24,23.3,22.4,21.8,21.1,20.2,18.9,18.3,17.2,16.4,15.7,14.3,13.6,12.3,11.3,10.9,9.45,8.51,7.33,6.51,5.29,4.28,3.36,2.06,2.07,2.11,1.58,2.35,1.93,2,1.24,1.62,1.38,1.4,1.56,1.43,1.61,1.32,1.59,2.01,1.79,1.88,1.65,1.61,1.68,1.53,1.61,1.56,1.92,1.15,1.89,1.8,1.51,1.88,1.63,2.33,2.96,5.33,7.41,9.19,11.3,13.5,15.3,17.2,18.8,20.7,22.5,24,25.6,26.9,28.3,30,31.1,32.4,33.7,35.1,36.2,37.5,38.6,40,40.9,41.8,42.9,43.7,44.6,45.1,45.9,46.3,46.6,46.6,46.7,46.6,46.6,46.6,46.7,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.5,46.5,46.4,46.4,45.9,45.4,44.9,44.3,43.6,42.8,42,41.2,40.3,39.4,38.5,37.6,36.4,35.4,34.3,33,32,30.7,29.4,28.2,27,26,24.7,23.6,22.2,20.9,19.6,18.5,17.2,15.5,14.3,13,11.3,10.3,8.34,7.2,5.62,4.49,2.5,1.41,0.382,0.025,0.0282,0.0315,0.0347,0.0379,0.0411,0.0444,0.0476,0.0508,0.054,0.0573,0.0605,0.0637,0.067,0.0702,0.0734,0.0766,0.0799,0.0831,0.0863,0.0895,0.0928,0.096,0.0992,0.102,0.106,0.109,0.112,0.115,0.119,0.122,0.125,0.128,0.132,0.135,0.138,0.141,0.144,0.148,0.151,0.154,0.157,0.161,0.164,0.167,0.17,0.173,0.177,0.18,0.183,0.186,0.19,0.193,0.196,0.199,0.203,0.206,0.209,0.212,0.215,0.219,0.222,0.225,0.228,0.232,0.235,1.98,5.13,8.15,11.1,14,16.5,19.4,22,24.6,27.1,29.4,31.9,34.1,36.2,38.3,40,42,43.3,45,45.8,46.2,46.6,46.6,46.6,46.6,46.6,46.7,46.7,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.6,46.5,46.3,45,43.9,42.9,41.9,40.7,39.7,38,36.6,35.1,33.4,31.5,29.7,27.8,25.9,23.7,21.7,19.9,17.9,16.3,14.1,11.8,10,7.41,5.78,3.03,1.18,0.088,0.0904,0.0929,0.0953,0.0977,0.1,0.103,0.105,0.107,0.11,0.112,0.115,0.117,0.12,0.122,0.124,0.127,0.129,0.132,0.134,0.137,0.139,0.141,0.144,0.146,0.149,0.151,0.153,0.156,0.158,0.161,0.163,0.166,0.168,0.17,0.173,0.175,0.178,0.18,0.183,0.185,0.187,0.19,0.192,0.195,0.197,0.2,0.202,0.204,0.207,0.209,0.212,0.214,0.217,0.219,0.221,0.224,0.226,0.229,0.231,0.234,0.236,0.238,0.241,0.243,0.246,0.248,0.25,0.253,0.255,0.258,0.26,0.263,0.265,0.267,0.27,0.272,0.275,0.277,0.28,4.98,9.21,14.5,20.3,25.4,31.4,35.3,40.9,46.1,54.4,58.7,64.2,68.1,73.1,76.9,81.1,85.3,88.7,91.9,96,98.8,101,103,104,105,106,106,106,107,110,113,117,121,125,129,133,138,142,147,152,157,154,158,159,161,162,163,164,165,166,167,168,169,170,170,171,172,173,174,174,175,176,177,177,178,178,179,180,180,181,180,181,182,183,183,184,185,186,186,187,188,188,189,188,190,191,191,191,192,193,191,184,182,184,184,184,185,186,186,188,187,188,190,189,190,191,191,193,194,195,196,195,193,193,196,193,194,194,195,193,194,194,194,194,193,193,194,193,193,193,193,193,193,193,192,191,190,191,188,185,182,183,181,180,179,178,177,176,175,173,172,171,170,169,168,166,165,164,163,160,163,171,171,170,169,167,165,164,161,159,157,152,144,136,128,120,112,113,116,120,124,128,133,137,143,147,151,153,154,157,159,160,161,162,163,164,165,166,167,168,169,169,170,171,172,173,173,174,175,176,177,177,177,177,178,179,179,180,180,181,182,183,184,184,185,186,187,187,188,188,183,177,180,181,181,182,183,184,184,185,186,186,186,188,188,189,190,190,191,192,192,193,193,194,196,197,198,199,199,199,201,203,203,205,204,203,204,204,204,203,204,204,204,204,173];
+let PT_6 = [40.7,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.5,40.4,40.4,40.3,40.2,40.2,40.1,39.9,39.8,39.6,39.5,39.4,39.2,39.1,39,38.8,38.6,38.3,38.2,38,37.9,37.7,37.5,37.4,37.1,36.9,36.6,36.5,36.4,36.2,36,35.7,35.5,35.4,35.1,35,35,34.6,34.1,34.2,33.7,33.6,33.3,33.3,33,32.8,32.3,32.2,31.9,31.4,31.3,31,30.5,30.3,30,30,29.5,29.4,29.3,28.8,28.5,28,27.9,27.9,27.7,27.6,27.5,27.6,27.3,27.8,27.6,27.5,27.5,27.8,27.4,27.6,27.5,27.5,27.4,27.6,27.6,27.5,27.8,27.6,27.5,27.6,27.6,27.5,27.5,27.8,27.7,27.6,27.8,27.5,27.6,27.8,28.4,28.8,29.5,30.1,30.5,30.9,31.6,32.2,32.5,33.3,33.7,34.1,34.6,35,35.5,35.9,36.2,36.5,36.8,37.2,37.6,38.2,38.3,38.7,39.1,39.3,39.6,39.8,40.1,40.3,40.4,40.6,40.6,40.7,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.5,40.5,40.5,40.5,40.4,40.4,40.4,40.4,40,39.5,39.2,38.8,38.4,38.1,37.6,37.1,36.7,36.4,35.9,35.3,34.7,34.2,33.6,33,32.5,31.7,31.1,30.6,30,29.1,28.5,28,27.4,26.6,25.9,25.2,25.1,24.5,23.8,23,22.3,21.9,20.8,19.9,19.5,18.6,17.9,17.1,16,15.3,14.8,13.9,12.8,11.8,11.1,10.1,9.26,7.9,7.08,6.04,5.08,4.58,3.56,2.48,0.97,0.201,0.063,0.0668,0.0706,0.0744,0.0782,0.0819,0.0857,0.0895,0.0933,0.0971,0.101,0.105,0.108,0.112,0.116,0.12,0.124,0.127,0.131,0.135,0.139,0.143,0.146,0.15,0.154,0.158,0.162,0.165,0.169,0.173,0.177,0.18,0.184,0.188,0.192,0.196,0.199,0.203,1.21,2.65,5.08,6.68,8.61,10.8,12.6,14.5,16,17.5,19.3,20.7,22.3,23.6,25,26.1,27.5,28.8,30,31.2,32.6,33.8,34.8,35.8,36.9,37.6,38.5,39.2,39.9,40.3,40.4,40.6,40.5,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.5,40.5,40.5,40.4,39.8,39.3,38.9,38.2,37.6,36.8,36,35,34.1,33.3,32.4,31.2,30.2,29.3,28.2,26.8,25.7,24.6,23.4,22.1,20.8,19.6,18.3,17.4,15.9,14.6,13.3,12.3,10.5,9.36,7.87,6.53,4.95,3.66,2.13,0.752,0.088,0.0901,0.0921,0.0942,0.0962,0.0983,0.1,0.102,0.104,0.106,0.109,0.111,0.113,0.115,0.117,0.119,0.121,0.123,0.125,0.127,0.129,0.131,0.133,0.135,0.137,0.139,0.141,0.143,0.146,0.148,0.15,0.152,0.154,0.156,0.158,0.16,0.162,0.164,0.166,0.168,0.17,0.172,0.174,0.176,0.178,0.18,0.183,0.185,0.187,0.189,0.191,0.193,0.195,0.197,0.199,0.201,0.203,0.205,0.207,0.209,0.211,0.213,0.215,0.217,0.22,0.222,0.224,0.226,0.228,0.23,0.232,0.234,0.236,1.58,4.62,7.89,10.5,13.2,15.6,18.4,20.7,23.4,25.6,28,30.1,32.2,33.9,35.9,37.3,38.7,39.7,40.3,40.6,40.6,40.6,40.6,40.5,40.5,40.5,40.5,40.5,40.5,40.5,40.5,40.5,40.6,40.5,40.5,40.3,40.2,39,37.8,36.9,35.8,34.5,33.4,31.9,30.4,28.8,27.2,25.3,23.5,21.5,19.5,17.4,15.6,13.7,11.5,10.1,7.57,5.54,3.41,1.2,0.038,0.0398,0.0415,0.0433,0.0451,0.0468,0.0486,0.0504,0.0521,0.0539,0.0557,0.0575,0.0592,0.061,0.0628,0.0645,0.0663,0.0681,0.0698,0.0716,0.0734,0.0751,0.0769,0.0787,0.0804,0.0822,0.084,0.0857,0.0875,0.0893,0.0911,0.0928,0.0946,0.0964,0.0981,0.0999,0.102,0.103,0.105,0.107,0.109,0.111,0.112,0.114,0.116,0.118,0.119,0.121,0.123,0.125,0.126,0.128,0.13,0.132,0.133,0.135,0.137,0.139,0.141,0.142,0.144,0.146,0.148,0.149,0.151,0.153,0.155,0.156,0.158,0.16,0.162,0.164,0.165,0.167,0.169,0.171,0.172,0.174,0.176,0.178,0.179,0.181,0.183,0.185,0.187,0.188,0.19,0.192,0.194,0.195,0.197,0.199,0.201,0.202,0.204,3.13,7.4,11.8,15.7,19.6,23.1,26.9,30,32.8,35.5,37.3,38.8,39.6,40.2,40.4,40.5,40.5,40.5,40.5,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.6,40.5,39.5,38.1,36.6,34.7,33,30.1,28.2,26.2,24.2,21.9,19.7,17,14.7,12.2,9.74,7.14,4.29,1.51,0.044,0.0451,0.0461,0.0472,0.0483,0.0494,0.0504,0.0515,0.0526,0.0537,0.0547,0.0558,0.0569,0.058,0.059,0.0601,0.0612,0.0623,0.0633,0.0644,0.0655,0.0666,0.0676,0.0687,0.0698,0.0709,0.0719,0.073,0.0741,0.0752,0.0762,0.0773,0.0784,0.0795,0.0805,0.0816,0.0827,0.0838,0.0848,0.0859,0.087,0.0881,0.0891,0.0902,0.0913,0.0924,0.0934,0.0945,0.0956,0.0966,0.0977,0.0988,0.0999,0.101,0.102,0.103,0.104,0.105,0.106,0.107,0.108,0.11,0.111,0.112,0.113,0.114,0.115,0.116,0.117,0.118,0.119,0.12,0.121,0.122,0.124,0.125,0.126,0.127,0.128,0.129,0.13,0.131,0.132,0.133,0.134,0.135,0.136,0.137,0.139,0.14,0.141,0.142,0.143,0.144,5.14,11.5,17.4,24.3,29.9,36.9,42,50.6,61.2,66.7,72.5,78.1,82.8,87.5,90.9,94.8,97.6,101,103,104,105,106,106,108,111,115,120,124,128,133,138,142,145,148,150,152,154,155,156,155,157,158,159,160,162,162,164,164,165,167,168,168,169,170,171,172,172,173,174,175,174,175,176,177,178,179,179,181,181,182,182,183,183,185,186,186,186,181,175,179,179,180,180,181,182,183,183,183,183,185,186,186,186,187,188,188,190,190,191,192,194,195,196,197,196,200,199,202,203,203,203,202,203,203,201,201,204,203,203,202,202,154];
+
+let FT_1 = [0.138,0.131,0.124,0.117,0.122,0.128,0.133,0.138,0.145,0.152,0.159,0.169,0.179,0.199,0.209,0.219,0.266,0.335,0.357,0.425,0.467,0.509,0.551,0.595,0.639,0.682,0.729,0.773,0.816,0.859,0.903,0.947,0.989,1.01,1.05,1.1,1.12,1.16,1.2,1.22,1.27,1.29,1.33,1.36,1.38,1.42,1.44,1.46,1.5,1.53,1.57,1.59,1.61,1.63,1.68,1.7,1.72,1.76,1.78,1.8,1.82,1.87,1.89,1.93,1.95,1.97,2.01,2.03,2.06,2.1,2.12,2.14,2.16,2.21,2.23,2.25,2.29,2.31,2.33,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.35,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.34,2.33,2.29,2.23,2.18,2.11,2.07,2.02,1.96,1.89,1.85,1.78,1.74,1.67,1.62,1.56,1.51,1.44,1.39,1.33,1.26,1.19,1.12,1.06,0.984,0.892,0.819,0.725,0.64,0.569,0.451,0.345,0.249,0.186,0.123,0.056,0.028,0.007,0.00988,0.0128,0.0156,0.0185,0.0214,0.0243,0.0271,0.105,0.117,0.128,0.152,0.163,0.173,0.195,0.238,0.284,0.326,0.371,0.44,0.487,0.532,0.577,0.62,0.641,0.686,0.73,0.773,0.793,0.836,0.879,0.92,0.964,0.984,1.03,1.07,1.09,1.13,1.15,1.2,1.22,1.24,1.28,1.3,1.35,1.35,1.36,1.37,1.39,1.43,1.45,1.47,1.5,1.54,1.56,1.58,1.6,1.62,1.67,1.69,1.71,1.75,1.77,1.79,1.81,1.85,1.88,1.9,1.94,1.96,1.98,2,2.05,2.07,2.09,2.13,2.15,2.17,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.19,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.17,2.17,2.17,2.17,2.17,2.13,2.06,2.01,1.95,1.9,1.84,1.79,1.74,1.67,1.63,1.58,1.52,1.47,1.4,1.36,1.3,1.25,1.18,1.13,1.07,0.998,0.903,0.834,0.771,0.699,0.629,0.558,0.464,0.368,0.268,0.198,0.123,0.028,0.007,0.00964,0.0123,0.0149,0.0175,0.0202,0.0228,0.0255,0.0281,0.0307,0.0334,0.063,0.09,0.0987,0.107,0.116,0.19,0.21,0.252,0.295,0.339,0.404,0.449,0.472,0.515,0.56,0.606,0.627,0.671,0.692,0.736,0.779,0.799,0.844,0.885,0.906,0.951,0.992,1.01,1.04,1.08,1.1,1.12,1.17,1.19,1.21,1.25,1.27,1.29,1.31,1.35,1.38,1.4,1.42,1.44,1.46,1.5,1.53,1.55,1.57,1.59,1.61,1.63,1.67,1.69,1.71,1.73,1.75,1.78,1.82,1.84,1.86,1.88,1.91,1.95,1.97,1.99,2.03,2.05,2.07,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.09,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.08,2.07,2.07,2.03,1.99,1.92,1.87,1.83,1.76,1.72,1.68,1.61,1.57,1.52,1.47,1.43,1.36,1.32,1.25,1.2,1.16,1.09,1.03,0.961,0.919,0.851,0.76,0.688,0.623,0.553,0.48,0.381,0.285,0.21,0.135,0.034,0.01,0.0116,0.0133,0.0149,0.0166,0.0182,0.0199,0.0215,0.0232,0.0248,0.0265,0.0281,0.0298,0.0314,0.0331,0.0347,0.0364,0.116,0.181,0.226,0.247,0.291,0.335,0.377,0.421,0.442,0.486,0.529,0.55,0.593,0.614,0.636,0.677,0.699,0.721,0.763,0.784,0.826,0.848,0.89,0.911,0.932,0.975,0.996,1.02,1.04,1.06,1.08,1.1,1.12,1.15,1.19,1.21,1.23,1.25,1.27,1.29,1.32,1.34,1.36,1.38,1.4,1.44,1.46,1.49,1.51,1.53,1.55,1.59,1.61,1.63,1.66,1.68,1.7,1.74,1.76,1.78,1.8,1.82,1.87,1.89,1.91,1.91,1.91,1.91,1.91,1.9,1.9,1.9,1.9,1.9,1.9,1.9,1.9,1.9,1.89,1.89,1.89,1.89,1.89,1.89,1.89,1.89,1.89,1.9,1.9,1.9,1.9,1.9,1.9,1.91,1.91,1.91,1.9,1.87,1.82,1.78,1.74,1.67,1.63,1.58,1.53,1.49,1.45,1.4,1.36,1.29,1.27,1.2,1.18,1.12,1.03,0.983,0.937,0.87,0.802,0.74,0.67,0.623,0.556,0.491,0.418,0.342,0.274,0.21,0.142,0.043,0.019,0.0208,0.0226,0.0245,0.0263,0.0281,0.0299,0.0318,0.0336,0.0354,0.0372,0.0391,0.0409,0.0427,0.0445,0.0464,0.0482,0.073,0.148,0.191,0.212,0.233,0.256,0.278,0.323,0.345,0.389,0.41,0.432,0.474,0.518,0.539,0.56,0.581,0.625,0.636,0.668,0.679,0.711,0.732,0.754,0.776,0.797,0.819,0.841,0.862,0.904,0.915,0.926,0.947,0.968,0.989,1.01,1.03,1.05,1.07,1.09,1.14,1.16,1.18,1.2,1.22,1.24,1.26,1.3,1.32,1.37,1.41,1.42,1.45,1.47,1.49,1.51,1.54,1.56,1.6,1.62,1.64,1.65,1.66,1.68,1.7,1.72,1.72,1.71,1.71,1.7,1.69,1.68,1.68,1.68,1.68,1.68,1.68,1.68,1.67,1.67,1.67,1.67,1.67,1.67,1.67,1.67,1.67,1.67,1.66,1.66,1.66,1.66,1.66,1.67,1.68,1.7,1.69,1.68,1.64,1.59,1.55,1.5,1.45,1.41,1.37,1.29,1.25,1.18,1.13,1.09,1.05,1.03,0.979,0.938,0.891,0.848,0.801,0.757,0.694,0.606,0.537,0.491,0.421,0.375,0.305,0.26,0.213,0.191,0.113,0.021,0.0000000000000216,0.0015,0.003,0.0045,0.006,0.0075,0.009,0.0105,0.012,0.0135,0.015,0.0165,0.018,0.0195,0.021,0.0225,0.024,0.0255,0.027,0.0285,0.108,0.156,0.164,0.171,0.179,0.2,0.221,0.263,0.284,0.305,0.326,0.347,0.367,0.408,0.43,0.45,0.471,0.515,0.537,0.558,0.58,0.602,0.623,0.644,0.664,0.685,0.706,0.727,0.738,0.748,0.79,0.801,0.812,0.834,0.855,0.875,0.886,0.896,0.941,0.962,0.982,1,1.03,1.05,1.09,1.11,1.13,1.15,1.17,1.2,1.22,1.26,1.28,1.3,1.32,1.34,1.36,1.38,1.41,1.45,1.47,1.49,1.49,1.49,1.48,1.48,1.48,1.47,1.47,1.47,1.46,1.45,1.44,1.44,1.44,1.43,1.43,1.43,1.43,1.42,1.42,1.42,1.41];
+let FT_2 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.228,0.33,0.395,0.441,0.488,0.533,0.577,0.619,0.664,0.687,0.729,0.751,0.792,0.814,0.859,0.9,0.922,0.966,0.986,1.03,1.05,1.09,1.11,1.14,1.18,1.2,1.22,1.24,1.29,1.31,1.33,1.35,1.37,1.42,1.44,1.46,1.48,1.5,1.54,1.57,1.59,1.61,1.65,1.67,1.69,1.72,1.74,1.78,1.8,1.82,1.86,1.88,1.91,1.93,1.95,1.99,2.01,2.03,2.05,2.07,2.12,2.14,2.16,2.17,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.18,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.17,2.16,2.16,2.16,2.16,2.16,2.16,2.16,2.16,2.14,2.09,2.05,2,1.93,1.89,1.84,1.8,1.73,1.69,1.62,1.57,1.53,1.49,1.42,1.37,1.33,1.28,1.22,1.17,1.1,1.05,0.984,0.917,0.85,0.784,0.713,0.641,0.54,0.469,0.371,0.279,0.19,0.094,0.069,0.018,0.0204,0.0227,0.0251,0.0275,0.0298,0.0322,0.0345,0.0369,0.0393,0.0416,0.071,0.0768,0.0825,0.0883,0.094,0.164,0.206,0.252,0.322,0.369,0.415,0.458,0.501,0.546,0.568,0.613,0.654,0.677,0.719,0.741,0.784,0.805,0.827,0.869,0.889,0.91,0.931,0.975,0.996,1.02,1.06,1.08,1.12,1.15,1.19,1.21,1.23,1.25,1.29,1.32,1.34,1.36,1.38,1.4,1.44,1.46,1.49,1.51,1.53,1.55,1.57,1.61,1.63,1.65,1.67,1.7,1.72,1.74,1.78,1.8,1.82,1.84,1.86,1.89,1.91,1.95,1.97,1.99,2.02,2.04,2.06,2.08,2.08,2.08,2.08,2.08,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.07,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.06,2.01,1.97,1.92,1.88,1.83,1.79,1.72,1.67,1.63,1.58,1.52,1.47,1.43,1.38,1.32,1.28,1.23,1.17,1.1,1.05,0.982,0.936,0.869,0.826,0.762,0.694,0.623,0.548,0.479,0.386,0.311,0.192,0.123,0.047,0.018,0.0198,0.0216,0.0234,0.0253,0.0271,0.0289,0.0307,0.0325,0.0343,0.0361,0.0379,0.0398,0.0416,0.0434,0.0452,0.047,0.12,0.185,0.23,0.274,0.316,0.337,0.38,0.4,0.441,0.486,0.53,0.552,0.596,0.618,0.66,0.683,0.704,0.749,0.771,0.793,0.815,0.836,0.878,0.899,0.921,0.942,0.963,0.985,1.01,1.03,1.05,1.07,1.11,1.13,1.16,1.18,1.2,1.22,1.26,1.28,1.3,1.32,1.35,1.37,1.39,1.41,1.43,1.47,1.5,1.52,1.54,1.56,1.58,1.6,1.62,1.64,1.69,1.71,1.73,1.75,1.77,1.79,1.84,1.86,1.88,1.9,1.91,1.92,1.92,1.92,1.92,1.92,1.92,1.92,1.92,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.91,1.9,1.9,1.9,1.9,1.9,1.9,1.9,1.88,1.84,1.79,1.74,1.7,1.66,1.62,1.57,1.52,1.45,1.41,1.37,1.32,1.28,1.23,1.19,1.14,1.08,1.01,0.962,0.917,0.872,0.805,0.76,0.695,0.625,0.555,0.49,0.443,0.375,0.281,0.186,0.116,0.089,0.045,0.017,0.0184,0.0198,0.0212,0.0226,0.0241,0.0255,0.0269,0.0283,0.0297,0.0311,0.0325,0.0339,0.0354,0.0368,0.0382,0.0396,0.11,0.134,0.155,0.175,0.219,0.288,0.3,0.357,0.4,0.421,0.465,0.472,0.479,0.486,0.507,0.528,0.549,0.617,0.66,0.682,0.702,0.724,0.746,0.767,0.788,0.809,0.853,0.874,0.894,0.917,0.938,0.959,0.98,0.991,1.02,1.03,1.04,1.06,1.09,1.11,1.12,1.13,1.15,1.17,1.19,1.21,1.22,1.23,1.27,1.32,1.33,1.34,1.35,1.36,1.38,1.4,1.44,1.47,1.49,1.53,1.55,1.57,1.59,1.62,1.64,1.65,1.66,1.67,1.68,1.67,1.66,1.66,1.65,1.65,1.65,1.65,1.65,1.65,1.65,1.64,1.64,1.64,1.64,1.64,1.64,1.64,1.64,1.64,1.65,1.65,1.65,1.65,1.66,1.68,1.66,1.64,1.59,1.55,1.48,1.44,1.39,1.35,1.3,1.24,1.2,1.18,1.14,1.11,1.07,1.05,1.01,0.983,0.941,0.92,0.876,0.833,0.79,0.747,0.649,0.576,0.51,0.465,0.396,0.349,0.306,0.211,0.144,0.099,0.073,0.044,0.017,0.0186,0.0202,0.0217,0.0233,0.0249,0.0265,0.0281,0.0296,0.0312,0.0328,0.0344,0.0359,0.0375,0.0391,0.0407,0.0423,0.0438,0.0454,0.101,0.144,0.166,0.176,0.186,0.206,0.229,0.251,0.261,0.295,0.363,0.385,0.427,0.448,0.469,0.49,0.5,0.511,0.532,0.554,0.565,0.599,0.62,0.642,0.686,0.696,0.727,0.737,0.748,0.769,0.791,0.812,0.833,0.844,0.855,0.875,0.895,0.916,0.937,0.957,0.999,1.01,1.02,1.04,1.06,1.08,1.11,1.13,1.15,1.17,1.19,1.21,1.23,1.28,1.3,1.32,1.34,1.36,1.38,1.4,1.42,1.44,1.47,1.47,1.47,1.48,1.48,1.49,1.51,1.49,1.48,1.47,1.47,1.46,1.46,1.46,1.46,1.46,1.45,1.45,1.45,1.45,1.44];
+let FT_3 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.086,0.257,0.299,0.345,0.392,0.435,0.48,0.525,0.566,0.609,0.653,0.674,0.696,0.74,0.763,0.808,0.831,0.876,0.898,0.92,0.964,0.984,1,1.03,1.05,1.07,1.09,1.11,1.16,1.18,1.2,1.22,1.24,1.26,1.29,1.33,1.35,1.37,1.39,1.41,1.43,1.46,1.48,1.5,1.52,1.54,1.56,1.58,1.62,1.64,1.67,1.69,1.71,1.73,1.75,1.78,1.82,1.84,1.86,1.88,1.9,1.92,1.94,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.96,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.95,1.94,1.94,1.94,1.94,1.94,1.94,1.9,1.85,1.81,1.77,1.72,1.65,1.61,1.56,1.52,1.47,1.43,1.38,1.34,1.29,1.25,1.2,1.14,1.09,1.05,1,0.94,0.869,0.801,0.736,0.691,0.624,0.555,0.443,0.351,0.276,0.205,0.098,0.018,0.0193,0.0207,0.022,0.0233,0.0247,0.026,0.0273,0.0287,0.03,0.0313,0.0327,0.034,0.0353,0.0367,0.038,0.0393,0.0407,0.139,0.188,0.228,0.25,0.293,0.314,0.359,0.403,0.424,0.466,0.511,0.553,0.575,0.619,0.641,0.683,0.705,0.728,0.749,0.77,0.791,0.813,0.859,0.879,0.9,0.921,0.943,0.963,1.01,1.03,1.05,1.07,1.09,1.11,1.13,1.16,1.18,1.2,1.22,1.24,1.26,1.27,1.28,1.31,1.33,1.35,1.39,1.41,1.43,1.45,1.47,1.5,1.52,1.54,1.56,1.58,1.6,1.64,1.66,1.68,1.71,1.73,1.75,1.77,1.79,1.79,1.79,1.79,1.79,1.79,1.79,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.78,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.77,1.76,1.72,1.68,1.64,1.59,1.55,1.5,1.46,1.41,1.37,1.32,1.28,1.23,1.19,1.17,1.12,1.07,0.986,0.944,0.899,0.831,0.789,0.724,0.679,0.611,0.546,0.471,0.401,0.326,0.26,0.196,0.079,0.052,0.029,0.009,0.0104,0.0119,0.0133,0.0147,0.0162,0.0176,0.0191,0.0205,0.0219,0.0234,0.0248,0.0262,0.0277,0.0291,0.0306,0.032,0.125,0.193,0.213,0.223,0.234,0.277,0.299,0.32,0.343,0.386,0.408,0.451,0.474,0.517,0.54,0.585,0.607,0.628,0.65,0.671,0.692,0.735,0.756,0.777,0.798,0.82,0.83,0.841,0.882,0.903,0.913,0.924,0.946,0.966,0.988,1.01,1.03,1.05,1.06,1.07,1.09,1.12,1.14,1.16,1.18,1.2,1.22,1.24,1.29,1.33,1.34,1.37,1.39,1.41,1.43,1.46,1.48,1.5,1.52,1.54,1.56,1.58,1.6,1.63,1.64,1.65,1.64,1.64,1.63,1.63,1.62,1.6,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.61,1.62,1.62,1.62,1.62,1.62,1.62,1.62,1.62,1.62,1.62,1.63,1.62,1.61,1.6,1.56,1.51,1.47,1.43,1.38,1.34,1.29,1.25,1.21,1.17,1.12,1.1,1.06,1.03,0.989,0.944,0.901,0.881,0.835,0.792,0.749,0.651,0.589,0.525,0.453,0.409,0.34,0.265,0.222,0.179,0.074,0.0615,0.00000000000000455,0.00105,0.0021,0.00315,0.0042,0.00525,0.0063,0.00735,0.0084,0.00945,0.0105,0.0116,0.0126,0.0137,0.0147,0.0158,0.0168,0.0179,0.0189,0.02,0.116,0.159,0.182,0.192,0.202,0.244,0.265,0.285,0.306,0.328,0.349,0.37,0.413,0.424,0.434,0.454,0.476,0.518,0.54,0.561,0.604,0.626,0.647,0.669,0.69,0.711,0.733,0.754,0.775,0.796,0.817,0.828,0.838,0.86,0.88,0.89,0.9,0.921,0.944,0.964,0.985,1.01,1.03,1.05,1.07,1.09,1.11,1.13,1.16,1.18,1.2,1.22,1.24,1.26,1.3,1.33,1.35,1.37,1.39,1.41,1.43,1.45,1.46,1.46,1.47,1.48,1.49,1.5,1.49,1.48,1.45,1.45,1.45,1.45,1.45,1.44,1.44,1.44,1.44,1.43,1.43,1.43,1.38];
+let FT_4 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0235,0.0236,0.0236,0.0237,0.0237,0.0238,0.0238,0.0239,0.0239,0.125,0.138,0.15,0.248,0.325,0.525,0.734,0.803,0.847,0.892,0.958,1.03,1.07,1.11,1.16,1.18,1.22,1.26,1.31,1.35,1.39,1.42,1.46,1.47,1.48,1.5,1.55,1.57,1.61,1.63,1.67,1.72,1.74,1.78,1.8,1.85,1.87,1.89,1.94,1.96,2,2.04,2.07,2.09,2.13,2.15,2.2,2.22,2.26,2.3,2.31,2.32,2.32,2.34,2.36,2.37,2.39,2.4,2.41,2.43,2.44,2.45,2.46,2.47,2.5,2.51,2.52,2.54,2.56,2.6,2.64,2.69,2.73,2.8,2.84,2.89,2.93,2.95,2.95,2.94,2.93,2.93,2.93,2.93,2.93,2.93,2.93,2.93,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.92,2.91,2.91,2.91,2.84,2.75,2.65,2.6,2.55,2.53,2.51,2.47,2.44,2.42,2.38,2.36,2.34,2.3,2.25,2.2,2.14,2.07,2,1.96,1.91,1.81,1.75,1.7,1.63,1.58,1.51,1.46,1.37,1.32,1.26,1.19,1.12,1.01,0.938,0.832,0.678,0.549,0.4,0.259,0.133,0.019,0.0215,0.024,0.0265,0.029,0.0315,0.034,0.0365,0.039,0.0415,0.066,0.11,0.178,0.244,0.318,0.43,0.554,0.786,0.854,0.895,0.917,0.939,0.985,1.03,1.05,1.09,1.14,1.18,1.22,1.24,1.29,1.31,1.33,1.37,1.39,1.44,1.46,1.48,1.52,1.54,1.58,1.6,1.65,1.67,1.71,1.72,1.73,1.78,1.8,1.82,1.86,1.91,1.93,1.95,1.97,2.01,2.03,2.05,2.07,2.08,2.09,2.1,2.14,2.16,2.17,2.18,2.19,2.2,2.22,2.23,2.23,2.24,2.25,2.26,2.27,2.28,2.28,2.33,2.37,2.39,2.43,2.48,2.03,2.55,2.59,2.64,2.66,2.66,2.66,2.65,2.65,2.65,2.65,2.65,2.65,2.65,2.65,2.65,2.64,2.64,2.64,2.64,2.64,2.64,2.64,2.64,2.61,2.52,2.44,2.37,2.32,2.28,2.26,2.24,2.21,2.19,2.17,2.16,2.15,2.13,2.08,2.04,2,1.95,1.89,1.84,1.8,1.76,1.69,1.65,1.6,1.56,1.44,1.4,1.36,1.29,1.25,1.16,1.12,1.05,0.947,0.905,0.79,0.662,0.534,0.384,0.18,0.059,0.037,0.008,0.00983,0.0117,0.0135,0.0153,0.0172,0.019,0.0208,0.0227,0.0245,0.0263,0.0282,0.109,0.131,0.201,0.272,0.438,0.771,0.819,0.866,0.932,0.974,1.02,1.04,1.05,1.08,1.1,1.14,1.17,1.19,1.21,1.25,1.27,1.29,1.31,1.36,1.38,1.4,1.42,1.44,1.48,1.49,1.53,1.55,1.59,1.61,1.63,1.65,1.69,1.71,1.73,1.76,1.8,1.82,1.86,1.87,1.88,1.9,1.91,1.91,1.92,1.93,1.95,1.95,1.96,1.96,1.96,1.97,1.98,1.98,1.99,2,2,2.01,2.02,2.03,2.08,2.1,1.71,1.73,2.18,1.46,1.66,2.28,2.32,2.34,2.36,2.37,2.34,2.35,2.36,2.36,2.37,2.38,2.39,2.26,2.14];
+let FT_5 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.205,0.594,0.685,0.707,0.751,0.772,0.815,0.836,0.877,0.92,0.941,1,1.03,1.07,1.11,1.13,1.16,1.2,1.22,1.24,1.28,1.3,1.32,1.37,1.39,1.41,1.45,1.47,1.49,1.52,1.56,1.58,1.6,1.62,1.66,1.68,1.7,1.73,1.77,1.79,1.81,1.83,1.85,1.89,1.91,1.93,1.96,1.97,1.98,1.99,2.02,2.04,2.05,2.06,2.07,2.08,2.08,2.09,2.1,2.1,2.12,2.14,2.15,2.16,2.17,2.18,2.21,2.25,2.3,2.34,2.38,2.43,2.47,2.5,2.54,2.54,2.54,2.54,2.53,2.53,2.53,2.53,2.53,2.53,2.53,2.53,2.53,2.53,2.52,2.52,2.52,2.52,2.52,2.52,2.45,2.38,2.29,2.22,2.2,2.18,2.15,2.13,2.11,2.1,2.07,2.05,2.04,2.01,1.97,1.92,1.88,1.83,1.77,1.72,1.68,1.61,1.56,1.5,1.45,1.38,1.28,1.24,1.17,1.11,1.04,0.97,0.875,0.805,0.737,0.611,0.438,0.292,0.113,0.01,0.0125,0.015,0.0175,0.02,0.0225,0.025,0.0275,0.03,0.0325,0.035,0.0375,0.04,0.0425,0.227,0.509,0.637,0.679,0.7,0.743,0.765,0.776,0.81,0.831,0.873,0.893,0.935,0.956,0.978,1,1.04,1.06,1.08,1.11,1.13,1.15,1.19,1.21,1.24,1.26,1.28,1.32,1.34,1.36,1.39,1.41,1.43,1.45,1.47,1.49,1.53,1.56,1.58,1.6,1.62,1.64,1.68,1.7,1.73,1.75,1.77,1.78,1.79,1.8,1.8,1.81,1.81,1.82,1.83,1.84,1.85,1.85,1.86,1.87,1.88,1.89,1.89,1.9,1.91,1.93,1.94,1.98,2,2.02,2.07,2.12,2.16,2.18,2.2,2.25,2.24,2.23,2.23,2.22,2.21,2.21,2.2,2.19,2.18,2.18,2.17];
+let FT_6 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.072,0.0825,0.093,0.103,0.114,0.135,0.18,0.253,0.327,0.455,0.526,0.591,0.66,0.703,0.794,0.838,0.86,0.88,0.901,0.922,0.965,0.986,1.01,1.05,1.07,1.11,1.14,1.16,1.2,1.22,1.24,1.26,1.31,1.33,1.35,1.37,1.41,1.43,1.45,1.47,1.52,1.54,1.56,1.58,1.6,1.64,1.66,1.68,1.7,1.75,1.77,1.79,1.83,1.88,1.92,1.94,1.95,1.96,1.97,1.97,1.98,1.99,1.99,2,2.01,2.02,2.02,2.03,2.04,2.07,2.07,2.08,2.08,2.08,2.09,2.11,2.13,2.17,2.21,2.25,2.27,2.35,2.39,2.41,2.43,2.45,2.45,2.45,2.45,2.45,2.45,2.44,2.44,2.44,2.44,2.44,2.43];
+
+let ET_1 = [8.19,7.63,7.63,8.37,8.34,9.13,9,9.22,9.93,9.67,10.7,10.7,11.3,11.9,12.1,12.4,13.3,14.8,15.8,16.6,17.5,18.4,20,21.3,22.6,23.8,24.9,26.6,28.1,29.4,31.2,32.4,34.1,35.5,37.1,38.6,40.6,42.5,44.5,45.9,48.1,50.2,52.4,54.3,56.1,57.7,60,62.7,64.8,66.4,68.4,71.5,73.7,76,78.5,81.2,83.3,86.8,89.2,91.9,94.4,103,107,110,114,117,121,125,129,134,136,140,145,149,153,157,162,169,172,172,173,174,175,175,174,175,174,175,174,174,174,173,174,174,174,175,175,174,175,174,175,174,176,174,175,174,175,174,175,174,175,174,167,159,151,141,134,126,119,113,105,98.3,92.2,83.2,76.9,72,67.3,62.6,58.8,54.6,51.2,46.8,43.4,39.3,35.7,32.7,29.8,26.9,24.3,21.7,18.9,16.9,14.3,12.6,11.2,10,9.24,9.08,8.08,7.55,7.96,7.84,7.97,8.83,9.03,9.44,9.65,10.1,10.4,11.1,10.9,11.5,12.6,13.2,14,15,16,17.2,18.1,19.2,20.4,21.6,22.8,24.5,25.2,27,28.2,29.6,31.6,33.1,33.9,35.9,37.8,39.1,41.1,42.6,44.3,46.4,48.8,50.6,51.8,54.2,56.2,56.8,58.7,61,62.8,64.5,67.1,69.4,71.4,74,76.8,78.8,81.4,84,86.9,90.2,93.4,95.7,105,108,111,114,118,122,125,130,133,137,141,144,149,153,158,163,165,166,167,165,167,166,167,167,167,166,166,167,167,165,166,165,166,167,167,166,166,166,167,167,167,166,166,166,166,166,167,166,167,165,158,149,140,132,125,117,111,104,97.3,91.2,80.9,76.2,70.9,67.1,61.9,58,53.8,49.8,46.5,43,39,36.2,33,29.6,26.3,24.6,21.8,19,17.4,15.2,13.2,12.4,10.3,9.61,8.81,8.53,8.03,7.77,8.03,7.8,8.27,8.6,8.79,9.07,9.58,9.87,10.5,10.4,10.4,11.3,12.7,13.1,14.4,14.9,15.5,17.2,18.2,19,19.7,21.5,22.8,23.8,24.8,26.5,27.8,29,30.4,32.1,33.7,35.2,36.8,38.5,40.6,42.1,44.1,45.6,47,49.5,51.1,52.7,55.1,57,58.5,60.9,62.8,65.2,67.7,70.1,72.3,74.6,76.9,79.6,81.1,84.4,87.2,90.2,92.9,96.7,105,108,112,115,118,121,125,130,133,136,141,145,149,154,158,163,164,164,165,162,164,164,164,164,165,164,164,164,164,164,164,164,164,165,164,165,164,164,165,164,165,165,165,164,165,165,164,164,165,158,149,141,134,125,118,112,105,98.4,91.9,82.1,77,73,68.1,62.7,59,54.9,50.9,47.7,43.6,39.8,36.7,33.6,30,27.4,25.1,22.5,19.9,17.8,15.9,14,12.1,11.1,9.94,9.04,8.5,8.09,7.71,8.18,8.18,8.4,8.47,8.75,9.35,8.97,9.38,9.96,10.2,10.3,10.8,11.2,11.9,12.7,14.1,14.9,15.7,16.3,17.2,18.5,19.4,20.8,21.6,22.7,24.2,25.7,27,27.9,29.6,30.8,32.7,34.2,35.4,37.1,38.9,40.5,42,44,45.5,47.5,49.2,51.2,53.1,54.7,56.7,58.3,60.3,62.5,64.5,67.2,69.3,71.1,73.5,76.9,79.5,81.5,82.4,85.8,89.6,92.1,93.5,104,107,111,114,117,120,125,128,133,137,141,145,149,154,159,161,160,161,161,160,160,160,160,161,160,160,161,160,161,160,160,160,160,159,160,160,161,160,160,160,159,160,160,161,160,160,161,159,147,140,132,125,118,111,104,97.1,86.4,81.2,75.7,70.9,66.4,62.3,58.3,54.6,50.5,46.6,43.2,39.5,36,32.7,30.1,26.8,24.5,22,21.3,17.5,15.7,14.3,12,11.9,10.3,9.4,9.11,8.63,7.98,7.6,7.83,7.7,8.58,9.57,9.38,9.12,9.7,9.56,10.5,10.6,10.5,11,12,12.8,13.1,14.1,14.5,15,16.2,17.5,18.3,18.8,20,21.1,22.3,23,24.3,25.5,27.4,28.9,29.5,30.9,32.2,33.8,35,37.4,38.5,40.6,41,43.9,45.2,47.3,48.7,50.6,52,54,56.2,57.7,60,61.8,63.6,65.6,68,70.4,73.1,75.1,77,80.6,83.5,86,90.1,93,96.6,104,108,111,116,119,123,126,130,133,138,141,146,149,153,156,157,157,157,157,157,157,158,156,158,156,157,156,157,157,158,157,159,158,157,156,157,157,157,156,157,157,157,157,158,157,154,146,138,130,122,115,109,102,95.3,81.5,79.2,72.4,67.9,63.7,60.2,55.8,52.2,48.7,44.7,41.7,41.7,35.4,31.8,28.2,25.5,23.4,20.8,18.7,16.8,15.4,14.1,12.9,11,10.3,9.82,9.5,8.7,8.19,7.6,7.22,8.27,7.91,8.42,8.61,8.83,9.16,9.57,9.66,9.95,9.99,10.5,10.9,12.1,11.9,12.5,13.9,14.3,14.2,15.2,16.1,17.3,17.3,18.4,19.6,20.6,21.5,22.1,23.7,24.6,26.1,27.2,28.7,30,31.9,32.8,34,35.6,36.7,38.3,40.3,41.6,43.1,44.8,46.5,49,49.9,52,53.4,55.9,58,59.8,62.3,64.8,67,69.4,72,73.2,76.4,79.3,82.4,85.5,88.6,91,94.2,97.9,106,110,113,117,120,124,126,132,134,139,140,143,148,150,150,152,154,153,152,153,152,152,152,152,152,153,152,152,154,153,154,154];
diff --git a/readme/measured_curve.json b/readme/measured_curve.json
new file mode 100644
index 0000000..44354b2
--- /dev/null
+++ b/readme/measured_curve.json
@@ -0,0 +1,49 @@
+{
+ "100": {
+ "is_valid": false,
+ "error": 0,
+ "ctrl_curve": { "50": 50 },
+ "flow_curve": { "50": 0.019667187500000002 },
+ "power_curve": { "50": 8.482656250000002 }
+ },
+ "125": {
+ "is_valid": false,
+ "error": 0,
+ "ctrl_curve": { "100": 100 },
+ "flow_curve": { "100": 0.0835 },
+ "power_curve": { "100": 10.4 }
+ },
+ "150": {
+ "is_valid": false,
+ "error": 0,
+ "ctrl_curve": { "200": 200 },
+ "flow_curve": { "200": 0.32146874999999997 },
+ "power_curve": { "200": 16.7125 }
+ },
+ "175": {
+ "is_valid": true,
+ "error": 0,
+ "ctrl_curve": { "0": 0, "350": 350, "550": 550, "600": 600, "1000": 1000 },
+ "flow_curve": {
+ "0": 0,
+ "350": 0.9294287109375,
+ "550": 0.941,
+ "600": 1.05,
+ "1000": 1.922000000000001
+ },
+ "power_curve": {
+ "0": 5.076000000000002,
+ "350": 39.11787109375,
+ "550": 64.8,
+ "600": 76.4,
+ "1000": 169.20000000000007
+ }
+ },
+ "200": {
+ "is_valid": false,
+ "error": 0,
+ "ctrl_curve": { "550": 550 },
+ "flow_curve": { "550": 1.8175000000000001 },
+ "power_curve": { "550": 94.55 }
+ }
+}
diff --git a/rotatingMachine.html b/rotatingMachine.html
new file mode 100644
index 0000000..c5d4bfc
--- /dev/null
+++ b/rotatingMachine.html
@@ -0,0 +1,297 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/rotatingMachine.js b/rotatingMachine.js
new file mode 100644
index 0000000..1205bc4
--- /dev/null
+++ b/rotatingMachine.js
@@ -0,0 +1,247 @@
+module.exports = function (RED) {
+ function rotatingMachine(config) {
+ RED.nodes.createNode(this, config);
+ var node = this;
+
+ try {
+ // Load Machine class and curve data
+ const Machine = require("./dependencies/machine/machine");
+ const OutputUtils = require("../generalFunctions/helper/outputUtils");
+
+ const machineConfig = {
+ general: {
+ name: config.name || "Default Machine",
+ id: node.id,
+ logging: {
+ enabled: config.eneableLog,
+ logLevel: config.logLevel
+ }
+ },
+ asset: {
+ supplier: config.supplier || "Unknown",
+ type: config.machineType || "generic",
+ subType: config.subType || "generic",
+ model: config.model || "generic",
+ machineCurve: config.machineCurve
+ }
+ };
+
+ const stateConfig = {
+ general: {
+ logging: {
+ enabled: config.eneableLog,
+ logLevel: config.logLevel
+ }
+ },
+ movement: {
+ speed: Number(config.speed)
+ },
+ time: {
+ starting: Number(config.startup),
+ warmingup: Number(config.warmup),
+ stopping: Number(config.shutdown),
+ coolingdown: Number(config.cooldown)
+ }
+ };
+
+ // Create machine instance
+ const m = new Machine(machineConfig, stateConfig);
+
+ // put m on node memory as source
+ node.source = m;
+
+ //load output utils
+ const output = new OutputUtils();
+
+ function updateNodeStatus() {
+ try {
+ const mode = m.currentMode;
+ const state = m.state.getCurrentState();
+ const flow = Math.round(m.measurements.type("flow").variant("predicted").position('downstream').getCurrentValue());
+ const power = Math.round(m.measurements.type("power").variant("predicted").position('upstream').getCurrentValue());
+ let symbolState;
+ switch(state){
+ case "off":
+ symbolState = "⬛";
+ break;
+ case "idle":
+ symbolState = "⏸️";
+ break;
+ case "operational":
+ symbolState = "⏵️";
+ break;
+ case "starting":
+ symbolState = "⏯️";
+ break;
+ case "warmingup":
+ symbolState = "🔄";
+ break;
+ case "accelerating":
+ symbolState = "⏩";
+ break;
+ case "stopping":
+ symbolState = "⏹️";
+ break;
+ case "coolingdown":
+ symbolState = "❄️";
+ break;
+ case "decelerating":
+ symbolState = "⏪";
+ break;
+ }
+ const position = m.state.getCurrentPosition();
+ const roundedPosition = Math.round(position * 100) / 100;
+
+ let status;
+ switch (state) {
+ case "off":
+ status = { fill: "red", shape: "dot", text: `${mode}: OFF` };
+ break;
+ case "idle":
+ status = { fill: "blue", shape: "dot", text: `${mode}: ${symbolState}` };
+ break;
+ case "operational":
+ status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}m³/h | ⚡${power}kW` };
+ break;
+ case "starting":
+ status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` };
+ break;
+ case "warmingup":
+ status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}m³/h | ⚡${power}kW` };
+ break;
+ case "accelerating":
+ status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}%| 💨${flow}m³/h | ⚡${power}kW` };
+ break;
+ case "stopping":
+ status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` };
+ break;
+ case "coolingdown":
+ status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` };
+ break;
+ case "decelerating":
+ status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} - ${roundedPosition}% | 💨${flow}m³/h | ⚡${power}kW` };
+ break;
+ default:
+ status = { fill: "grey", shape: "dot", text: `${mode}: ${symbolState}` };
+ }
+ return status;
+ } catch (error) {
+ node.error("Error in updateNodeStatus: " + error.message);
+ return { fill: "red", shape: "ring", text: "Status Error" };
+ }
+ }
+
+ function tick() {
+ try {
+ const status = updateNodeStatus();
+ node.status(status);
+
+ //get output
+ const classOutput = m.getOutput();
+ const dbOutput = output.formatMsg(classOutput, m.config, "influxdb");
+ const pOutput = output.formatMsg(classOutput, m.config, "process");
+
+ //console.log(pOutput);
+
+ //only send output on values that changed
+ let msgs = [];
+ msgs[0] = pOutput;
+ msgs[1] = dbOutput;
+
+ node.send(msgs);
+
+ } catch (error) {
+ node.error("Error in tick function: " + error);
+ node.status({ fill: "red", shape: "ring", text: "Tick Error" });
+ }
+ }
+
+ // register child on first output this timeout is needed because of node - red stuff
+ setTimeout(
+ () => {
+
+ /*---execute code on first start----*/
+ let msgs = [];
+
+ msgs[2] = { topic : "registerChild" , payload: node.id, positionVsParent: "upstream" };
+ msgs[3] = { topic : "registerChild" , payload: node.id, positionVsParent: "downstream" };
+
+ //send msg
+ this.send(msgs);
+ },
+ 100
+ );
+
+ //declare refresh interval internal node
+
+ setTimeout(
+ () => {
+ //---execute code on first start----
+ this.interval_id = setInterval(function(){ tick() },1000)
+ },
+ 1000
+ );
+
+ node.on("input", function(msg, send, done) {
+ try {
+
+ /* Update to complete event based node by putting the tick function after an input event */
+
+
+ switch(msg.topic) {
+ case 'registerChild':
+ const childId = msg.payload;
+ const childObj = RED.nodes.getNode(childId);
+ m.childRegistrationUtils.registerChild(childObj.source ,msg.positionVsParent);
+ break;
+ case 'setMode':
+ m.setMode(msg.payload);
+ break;
+ case 'execSequence':
+ const { source, action, parameter } = msg.payload;
+ m.handleInput(source, action, parameter);
+ break;
+ case 'execMovement':
+ const { source: mvSource, action: mvAction, setpoint } = msg.payload;
+ m.handleInput(mvSource, mvAction, Number(setpoint));
+ break;
+ case 'flowMovement':
+ const { source: fmSource, action: fmAction, setpoint: fmSetpoint } = msg.payload;
+ m.handleInput(fmSource, fmAction, Number(fmSetpoint));
+
+ break;
+ case 'emergencystop':
+ const { source: esSource, action: esAction } = msg.payload;
+ m.handleInput(esSource, esAction);
+ break;
+ case 'showCompleteCurve':
+ m.showCompleteCurve();
+ send({ topic : "Showing curve" , payload: m.showCompleteCurve() });
+ break;
+ case 'CoG':
+ m.showCoG();
+ send({ topic : "Showing CoG" , payload: m.showCoG() });
+ break;
+ }
+
+ if (done) done();
+ } catch (error) {
+ node.error("Error processing input: " + error.message);
+ if (done) done(error);
+ }
+ });
+
+ node.on('close', function(done) {
+ if (node.interval_id) clearTimeout(node.interval_id);
+ if (node.tick_interval) clearInterval(node.tick_interval);
+ if (done) done();
+ });
+
+ } catch (error) {
+ node.error("Fatal error in node initialization: " + error.stack);
+ node.status({fill: "red", shape: "ring", text: "Fatal Error"});
+ }
+ }
+
+ RED.nodes.registerType("rotatingMachine", rotatingMachine);
+};
\ No newline at end of file