//Remove all outliers that are > 1 + Percentile or < 1 - percentile from the average public static int RemoveOutliers(ref double[][] data, int index, double standardDeviationMultiple) { int removedEntries = 0; //Split into two seperate arrays List <double> array = new List <double>(); int length = data.GetLength(0); //We don't have enough samples to accurate check for outliers if (length < 30) { return(0); } //Copy the data we are working on to an array so we can calculate SD and our removal limits double average = 0.0; int count = 0; for (int i = 0; i < length; i++) { double value = data[i][index]; if (HelperMethods.IsValidDouble(value)) { average += value; count++; array.Add(value); } } average /= count; array.Sort(); double median = array[array.Count / 2]; double sumOfSquaresOfDifferences = array.Select(val => (val - average) * (val - average)).Sum(); double standardDeviation = Math.Sqrt(sumOfSquaresOfDifferences / array.Count); //If we have no variability then nothing to remove if (standardDeviation == 0.0) { return(0); } //Remove any value > or < standardDeviationMultiple*standardDeviation1 from the median double upperLimit = (standardDeviationMultiple * standardDeviation) + median; double lowerLimit = median - (standardDeviationMultiple * standardDeviation); for (int i = 0; i < length; i++) { double value = data[i][index]; //If the value is within range do nothing, if not zero this value so we ignore it if (value >= upperLimit || value <= lowerLimit) { data[i][index] = double.NaN; removedEntries++; } } return(removedEntries); }
public void SetFormat(CellFormat newFormat) { if (newFormat == cellFormat) { return; } cellFormat = newFormat; this.Value = ""; if (data.Length == 0) { return; } if (newFormat == CellFormat.Uninitialized) { newFormat = CellFormat.Average; } if (newFormat == CellFormat.Average) { dataValue = average; } else if (newFormat == CellFormat.Minimum) { dataValue = min; } else if (newFormat == CellFormat.Maximum) { dataValue = max; } else if (newFormat == CellFormat.StandardDeviation) { dataValue = standardDeviation; } else if (newFormat == CellFormat.Count) { dataValue = count; } else { throw new NotImplementedException(); } if (HelperMethods.IsValidDouble(dataValue) && dataValue >= this.ignoreLowerLimitValue && dataValue <= this.ignoreUpperLimitValue) { this.Value = String.Format("{0:0.00}", dataValue); cellCurrentlyIgnored = false; } else { cellCurrentlyIgnored = true; } }
public static List <double[]> ConditionData(List <double[]> unconditionedData) { List <double[]> conditionedData = new List <double[]>(); foreach (double[] value in unconditionedData) { if (HelperMethods.IsValidDouble(value[0]) && HelperMethods.IsValidDouble(value[1])) { conditionedData.Add(value); } } return (conditionedData); }
public void SetColour(double colourRange, double colourOffset) { if (HelperMethods.IsValidDouble(dataValue) && !cellCurrentlyIgnored) { double scalar = 360.0 / colourRange; double value = scalar * (dataValue - colourOffset); double H = (0.4 * (360.0 - value)) - 10.0; double S = 0.9; double B = 0.92; this.Style.BackColor = HelperMethods.HSVtoRGB(H, S, B); } else { this.Style.BackColor = Color.White; } }
//public void EmulatePCMSync() //{ // double[] returnArray = new double[2]; // returnArray[0] = double.NaN; // returnArray[1] = double.NaN; // if (fordPCMHelper == null) // { // MessageBox.Show("You must load a .CSV file first!"); // return; // } // if (!fordPCMHelper.fileLoaded) // { // MessageBox.Show("You must load a .HPT file first!"); // return; // } // int indexOfPulse, indexOfCalculatedLoad, indexOfMAP, indexOfTPS, indexOfCamAngle, indexOfRPM, indexOfMapPerAirmass, indexOfMapPerZeroAirmass, indexOfAirMass, indexOfFuelMassViaSD, indexOfFuelMassViaInjMs, indexOfCalculatedInjectorPW, indexOfCalculatedAFR, indexOfCalculatedInjectorPWTrimmed, indexOfFuelTrim; // indexOfCamAngle = Array.FindIndex(csvHeaders, x => (x.IndexOf("cam angle", StringComparison.OrdinalIgnoreCase) >= 0)); // indexOfRPM = Array.FindIndex(csvHeaders, x => (x.IndexOf("rpm", StringComparison.OrdinalIgnoreCase) >= 0)); // indexOfTPS = Array.FindIndex(csvHeaders, x => (x.IndexOf("ETC throttle", StringComparison.OrdinalIgnoreCase) >= 0)); // indexOfMAP = Array.FindIndex(csvHeaders, x => (x.IndexOf("MANIFOLD ABSOLUTE PRESSURE", StringComparison.OrdinalIgnoreCase) >= 0)); // indexOfPulse = Array.FindIndex(csvHeaders, x => (x.IndexOf("FUEL PULSEWIDTH", StringComparison.OrdinalIgnoreCase) >= 0)); // indexOfFuelTrim = Array.FindIndex(csvHeaders, x => (x.IndexOf("FUEL TRIM", StringComparison.OrdinalIgnoreCase) >= 0)); // if (indexOfMAP == -1) // { // MessageBox.Show("Couldn't find MAP entry in csv data!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); // return; // } // if (indexOfCamAngle == -1) // { // MessageBox.Show("Couldn't find cam angle entry in csv data!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); // return; // } // if (indexOfRPM == -1) // { // MessageBox.Show("Couldn't find rpm entry in csv data!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); // return; // } // if (indexOfTPS == -1) // { // MessageBox.Show("Couldn't find TPS entry in csv data, we will assume commanded lambda is always 1.0", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // return; // } // indexOfMapPerAirmass = Array.IndexOf(csvHeaders, "Map per Airmass"); // indexOfMapPerZeroAirmass = Array.IndexOf(csvHeaders, "Map per Zero Airmass"); // indexOfAirMass = Array.IndexOf(csvHeaders, "Calculated Airmass"); // indexOfFuelMassViaSD = Array.IndexOf(csvHeaders, "Calculated Fuel Mass via SD"); // indexOfFuelMassViaInjMs = Array.IndexOf(csvHeaders, "Calculated Fuel Mass via Pulsewidth"); // indexOfCalculatedInjectorPW = Array.IndexOf(csvHeaders, "Calculated Injector Pulsewidth"); // indexOfCalculatedInjectorPWTrimmed = Array.IndexOf(csvHeaders, "Calculated Injector Pulsewidth With Fuel Trim"); // indexOfCalculatedAFR = Array.IndexOf(csvHeaders, "Calculated Commanded AFR"); // indexOfCalculatedLoad = Array.IndexOf(csvHeaders, "Calculated Load"); // int arrayResizeAmount = 0; // if (indexOfMapPerAirmass == -1) // { // indexOfMapPerAirmass = csvHeaders.Length; // arrayResizeAmount++; // } // if (indexOfMapPerZeroAirmass == -1) // { // indexOfMapPerZeroAirmass = csvHeaders.Length + arrayResizeAmount; // arrayResizeAmount++; // } // if (indexOfAirMass == -1) // { // indexOfAirMass = csvHeaders.Length + arrayResizeAmount; // arrayResizeAmount++; // } // if (indexOfFuelMassViaSD == -1) // { // indexOfFuelMassViaSD = csvHeaders.Length + arrayResizeAmount; // arrayResizeAmount++; // } // if (indexOfFuelMassViaInjMs == -1) // { // indexOfFuelMassViaInjMs = csvHeaders.Length + arrayResizeAmount; // arrayResizeAmount++; // } // if (indexOfCalculatedInjectorPW == -1) // { // indexOfCalculatedInjectorPW = csvHeaders.Length + arrayResizeAmount; // arrayResizeAmount++; // } // if (indexOfCalculatedInjectorPWTrimmed == -1) // { // indexOfCalculatedInjectorPWTrimmed = csvHeaders.Length + arrayResizeAmount; // arrayResizeAmount++; // } // if (indexOfCalculatedAFR == -1) // { // indexOfCalculatedAFR = csvHeaders.Length + arrayResizeAmount; // arrayResizeAmount++; // } // if (indexOfCalculatedLoad == -1) // { // indexOfCalculatedLoad = csvHeaders.Length + arrayResizeAmount; // arrayResizeAmount++; // } // //Create new arrays with 3 extra values, // string[] newCsvHeaders = new string[csvHeaders.Length + arrayResizeAmount]; // double[][] newCsvData = new double[csvData.Length][]; ////Copy the previous data // Array.Copy(csvHeaders, newCsvHeaders, csvHeaders.Length); // csvHeaders.CopyTo(newCsvHeaders, 0); // for (int i = 0; i < csvData.Length; i++) // { // newCsvData[i] = new double[csvData[0].Length + arrayResizeAmount]; // for (int j = 0; j < csvData[0].Length; j++) // { // newCsvData[i][j] = csvData[i][j]; // } // } // //Add the new headers // newCsvHeaders[indexOfMapPerAirmass] = "Map per Airmass"; // newCsvHeaders[indexOfMapPerZeroAirmass] = "Map per Zero Airmass"; // newCsvHeaders[indexOfAirMass] = "Calculated Airmass"; // newCsvHeaders[indexOfFuelMassViaSD] = "Calculated Fuel Mass via SD"; // newCsvHeaders[indexOfFuelMassViaInjMs] = "Calculated Fuel Mass via Pulsewidth"; // newCsvHeaders[indexOfCalculatedInjectorPW] = "Calculated Injector Pulsewidth"; // newCsvHeaders[indexOfCalculatedInjectorPWTrimmed] = "Calculated Injector Pulsewidth With Fuel Trim"; // newCsvHeaders[indexOfCalculatedAFR] = "Calculated Commanded AFR"; // newCsvHeaders[indexOfCalculatedLoad] = "Calculated Load"; // //get stoich // double stoichAFR; // if (!fordPCMHelper.TryGetDouble(2300, out stoichAFR)) // { // MessageBox.Show("Couldn't read Stoich AFR value (2300), we will assume 14.64", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // stoichAFR = 14.64; // } // Boolean calculatePulseWidth = true; // //get low slope // double lowSlope; // if (!fordPCMHelper.TryGetDouble(12010, out lowSlope)) // { // MessageBox.Show("Couldn't read Injector Low Slope (12010), we will not be able to calculate pulse width!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // calculatePulseWidth = false; // } // lowSlope /= 3600.0; // //get high slope // double highSlope; // if (!fordPCMHelper.TryGetDouble(12011, out highSlope)) // { // MessageBox.Show("Couldn't read Injector High Slope (12010), we will not be able to calculate pulse width!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // calculatePulseWidth = false; // } // highSlope /= 3600.0; // //get offset // double breakpoint; // if (!fordPCMHelper.TryGetDouble(12012, out breakpoint)) // { // MessageBox.Show("Couldn't read Injector breakpoint (12012), we will not be able to calculate pulse width!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // calculatePulseWidth = false; // } // //get offset // double offset; // if (!fordPCMHelper.TryGetDouble(32050, out offset)) // { // MessageBox.Show("Couldn't read Injector offset at 12.0V (32050), we will not be able to calculate pulse width!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // //calculatePulseWidth = false; // offset = 0.00146499997936189; // } // double displacement; // if (!fordPCMHelper.TryGetDouble(50000, out displacement)) // { // MessageBox.Show("Couldn't read engine displacement (50000), we will assume 4.0L (0.00172 lb)", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // displacement = 0.00172; // } // Boolean lambdaErrorIssued = false; // //int samplesProcessed = 0; // int totalCount = csvData.Length; // //totalCount = 1200; // //for (int i = 0; i < csvData.GetLength(0); i++) // for (int i = 0; i < totalCount; i++) // //Parallel.For(0, csvData.GetLength(0), i => // //Parallel.For(0, totalCount, i => // { // double camAngle = csvData[i][indexOfCamAngle]; // double rpm = csvData[i][indexOfRPM]; // double map = csvData[i][indexOfMAP]; // double csvPulseWidthSeconds = csvData[i][indexOfPulse]; // double fuelTrim = csvData[i][indexOfFuelTrim]; // double TPS = -1.0; // double commandedLambda = 1.0; // //Get the commanded lambda value // if (indexOfTPS != -1) // { // TPS = csvData[i][indexOfTPS]; // TPS *= 1.25; // if (!fordPCMHelper.TryGetTableValue(rpm, TPS, 50151, out commandedLambda)) // { // if (!lambdaErrorIssued) MessageBox.Show(String.Format("Failed to get commanded lambda for {0}rpm {1}TPS%, we will use 1.0 from now on and no longer report anymore errors of this type", rpm, TPS)); // lambdaErrorIssued = true; // commandedLambda = 1.0; // } // } // newCsvData[i][indexOfCalculatedAFR] = commandedLambda; // //interpolate the map per airmass values // double mapPerAirmass; // double mapPerZeroAirmass; // if (!fordPCMHelper.TryGetTableValue(rpm, camAngle, 50051, out mapPerZeroAirmass)) // { // MessageBox.Show(String.Format("Failed to get Map per Zero Airmass for {0}rpm {1} Cam Angle, this is a fatal error, giving up, sorry bro", rpm, camAngle)); // return; // } // if (!fordPCMHelper.TryGetTableValue(rpm, camAngle, 50055, out mapPerAirmass)) // { // MessageBox.Show(String.Format("Failed to get Map per Airmass for {0}rpm {1} Cam Angle, this is a fatal error, giving up, sorry bro", rpm, camAngle)); // return; // } // newCsvData[i][ indexOfMapPerAirmass] = mapPerAirmass; // newCsvData[i][ indexOfMapPerZeroAirmass] = mapPerZeroAirmass; // //Calculate the airmass // double airMassLbs = (map - mapPerZeroAirmass) / mapPerAirmass; // newCsvData[i][indexOfAirMass] = airMassLbs; // //Calculate the load // newCsvData[i][indexOfCalculatedLoad] = airMassLbs / displacement; // //Calculate the fuelmass via the SD maps // double fuelMassViaSD = airMassLbs / (stoichAFR * commandedLambda); // newCsvData[i][indexOfFuelMassViaSD] = fuelMassViaSD; // //Calculate the pulse width // double calculatedPulseWidthSec; // //When below the breakpoint // if (fuelMassViaSD <= breakpoint) // { // calculatedPulseWidthSec = (fuelMassViaSD / lowSlope) + offset; // } // else // { // //fuelmass=high*(ms-(breakpoint*((1/low)-(1/high))+offset)) // //ms=(breakpoint*((1/low)-(1/high))+(fuelmass/highslope)+offset // calculatedPulseWidthSec = (breakpoint * ((1 / lowSlope) - (1 / highSlope))) + (fuelMassViaSD / highSlope) + offset; // } // double calculatedPulseWithMs = (calculatedPulseWidthSec - offset) * 1000.0; // //double calculatedPulseWithMs = (calculatedPulseWidthSec) * 1000.0; // //This is the low cut off // if (calculatedPulseWithMs < 0.2) calculatedPulseWithMs = 0.2; // double csvPulseWidthMs = csvPulseWidthSeconds * 1000.0; // newCsvData[i][indexOfCalculatedInjectorPW] = calculatedPulseWithMs; // double trimmedInjPulse = calculatedPulseWithMs * fuelTrim; // newCsvData[i][indexOfCalculatedInjectorPWTrimmed] = trimmedInjPulse; // //Calculate the fuel mass via a reverse calc of the injector pulse width // double breakpointMs = ((breakpoint / lowSlope) + offset) * 1000.0; // double fuelMassViaInjectorMs; // if(csvPulseWidthMs < breakpointMs) // { // //fuelMassViaInjectorMs = lowSlope * (csvPulseWidthSeconds - offset); // fuelMassViaInjectorMs = lowSlope * (csvPulseWidthSeconds); // } // else // { // //fuelMassViaInjectorMs = highSlope * (csvPulseWidthSeconds - (breakpoint * ((1 / lowSlope) - (1 / highSlope)) + offset)); // fuelMassViaInjectorMs = highSlope * (csvPulseWidthSeconds - (breakpoint * ((1 / lowSlope) - (1 / highSlope)))); // } // newCsvData[i][indexOfFuelMassViaInjMs] = fuelMassViaInjectorMs; // newCsvData[i][indexOfPulse] = newCsvData[i][indexOfPulse] * 1000.0; // } // //}); // //Calculate the R² value // double errorOfCalculatedInjPulse = GetRSquared(newCsvData, indexOfCalculatedInjectorPW, indexOfPulse, 1.0, 1000.0); // double errorOfCalculatedInjPulseTrimmed = GetRSquared(newCsvData, indexOfCalculatedInjectorPWTrimmed, indexOfPulse, 1.0, 1000.0); // double errorOfCalculatedFuelMassPulse = GetRSquared(newCsvData, indexOfFuelMassViaSD, indexOfFuelMassViaInjMs, 1.0, 1.0); // csvHeaders = newCsvHeaders; // csvData = newCsvData; // HelperMethods.WriteCSV(@"C:\temp\test.csv", csvHeaders, csvData); // returnArray[0] = errorOfCalculatedInjPulse; // returnArray[1] = errorOfCalculatedInjPulseTrimmed; // returnArray[2] = errorOfCalculatedFuelMassPulse; // if (HelperMethods.IsValidDouble(returnArray[0])) calculatedInjectorPulseErrorTextBox.Text = String.Format("{0:0.###}", returnArray[0]); // if (HelperMethods.IsValidDouble(returnArray[1])) calculatedInjectorTrimmedPulseErrorTextBox.Text = String.Format("{0:0.###}", returnArray[1]); // if (HelperMethods.IsValidDouble(returnArray[2])) calculatedFuelMassErrorTextBox.Text = String.Format("{0:0.###}", returnArray[2]); // return; //} async Task EmulatePCM(IProgress <int> progress, CancellationToken cts) { Boolean failed = false; double[] results = await Task.Run <double[]>(() => { double[] returnArray = new double[2]; returnArray[0] = double.NaN; returnArray[1] = double.NaN; if (fordPCMHelper == null) { MessageBox.Show("You must load a .CSV file first!"); return(returnArray); } if (!fordPCMHelper.fileLoaded) { MessageBox.Show("You must load a .HPT file first!"); return(returnArray); } int indexOfPulse, indexOfCalculatedLoad, indexOfMAP, indexOfTPS, indexOfCamAngle, indexOfRPM, indexOfMapPerAirmass, indexOfMapPerZeroAirmass, indexOfAirMass, indexOfFuelMassViaSD, indexOfFuelMassViaInjMs, indexOfCalculatedInjectorPW, indexOfCalculatedAFR, indexOfCalculatedInjectorPWTrimmed, indexOfFuelTrim; indexOfCamAngle = Array.FindIndex(csvHeaders, x => (x.IndexOf("cam angle", StringComparison.OrdinalIgnoreCase) >= 0)); indexOfRPM = Array.FindIndex(csvHeaders, x => (x.IndexOf("rpm", StringComparison.OrdinalIgnoreCase) >= 0)); indexOfTPS = Array.FindIndex(csvHeaders, x => (x.IndexOf("ETC throttle", StringComparison.OrdinalIgnoreCase) >= 0)); indexOfMAP = Array.FindIndex(csvHeaders, x => (x.IndexOf("MANIFOLD ABSOLUTE PRESSURE", StringComparison.OrdinalIgnoreCase) >= 0)); indexOfPulse = Array.FindIndex(csvHeaders, x => (x.IndexOf("FUEL PULSEWIDTH", StringComparison.OrdinalIgnoreCase) >= 0)); indexOfFuelTrim = Array.FindIndex(csvHeaders, x => (x.IndexOf("FUEL TRIM", StringComparison.OrdinalIgnoreCase) >= 0)); if (indexOfMAP == -1) { MessageBox.Show("Couldn't find MAP entry in csv data!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return(returnArray); } if (indexOfCamAngle == -1) { MessageBox.Show("Couldn't find cam angle entry in csv data!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return(returnArray); } if (indexOfRPM == -1) { MessageBox.Show("Couldn't find rpm entry in csv data!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return(returnArray); } if (indexOfTPS == -1) { MessageBox.Show("Couldn't find TPS entry in csv data, we will assume commanded lambda is always 1.0", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); return(returnArray); } indexOfMapPerAirmass = Array.IndexOf(csvHeaders, "Map per Airmass"); indexOfMapPerZeroAirmass = Array.IndexOf(csvHeaders, "Map per Zero Airmass"); indexOfAirMass = Array.IndexOf(csvHeaders, "Calculated Airmass"); indexOfFuelMassViaSD = Array.IndexOf(csvHeaders, "Calculated Fuel Mass via SD"); indexOfFuelMassViaInjMs = Array.IndexOf(csvHeaders, "Calculated Fuel Mass via Pulsewidth"); indexOfCalculatedInjectorPW = Array.IndexOf(csvHeaders, "Calculated Injector Pulsewidth"); indexOfCalculatedInjectorPWTrimmed = Array.IndexOf(csvHeaders, "Calculated Injector Pulsewidth With Fuel Trim"); indexOfCalculatedAFR = Array.IndexOf(csvHeaders, "Calculated Commanded AFR"); indexOfCalculatedLoad = Array.IndexOf(csvHeaders, "Calculated Load"); int arrayResizeAmount = 0; if (indexOfMapPerAirmass == -1) { indexOfMapPerAirmass = csvHeaders.Length; arrayResizeAmount++; } if (indexOfMapPerZeroAirmass == -1) { indexOfMapPerZeroAirmass = csvHeaders.Length + arrayResizeAmount; arrayResizeAmount++; } if (indexOfAirMass == -1) { indexOfAirMass = csvHeaders.Length + arrayResizeAmount; arrayResizeAmount++; } if (indexOfFuelMassViaSD == -1) { indexOfFuelMassViaSD = csvHeaders.Length + arrayResizeAmount; arrayResizeAmount++; } if (indexOfFuelMassViaInjMs == -1) { indexOfFuelMassViaInjMs = csvHeaders.Length + arrayResizeAmount; arrayResizeAmount++; } if (indexOfCalculatedInjectorPW == -1) { indexOfCalculatedInjectorPW = csvHeaders.Length + arrayResizeAmount; arrayResizeAmount++; } if (indexOfCalculatedInjectorPWTrimmed == -1) { indexOfCalculatedInjectorPWTrimmed = csvHeaders.Length + arrayResizeAmount; arrayResizeAmount++; } if (indexOfCalculatedAFR == -1) { indexOfCalculatedAFR = csvHeaders.Length + arrayResizeAmount; arrayResizeAmount++; } if (indexOfCalculatedLoad == -1) { indexOfCalculatedLoad = csvHeaders.Length + arrayResizeAmount; arrayResizeAmount++; } //Create new arrays with 3 extra values, string[] newCsvHeaders = new string[csvHeaders.Length + arrayResizeAmount]; double[][] newCsvData = new double[csvData.Length][]; //Copy the previous data Array.Copy(csvHeaders, newCsvHeaders, csvHeaders.Length); csvHeaders.CopyTo(newCsvHeaders, 0); for (int i = 0; i < csvData.Length; i++) { newCsvData[i] = new double[csvData[0].Length + arrayResizeAmount]; for (int j = 0; j < csvData[0].Length; j++) { newCsvData[i][j] = csvData[i][j]; } } //Add the new headers newCsvHeaders[indexOfMapPerAirmass] = "Map per Airmass"; newCsvHeaders[indexOfMapPerZeroAirmass] = "Map per Zero Airmass"; newCsvHeaders[indexOfAirMass] = "Calculated Airmass"; newCsvHeaders[indexOfFuelMassViaSD] = "Calculated Fuel Mass via SD"; newCsvHeaders[indexOfFuelMassViaInjMs] = "Calculated Fuel Mass via Pulsewidth"; newCsvHeaders[indexOfCalculatedInjectorPW] = "Calculated Injector Pulsewidth"; newCsvHeaders[indexOfCalculatedInjectorPWTrimmed] = "Calculated Injector Pulsewidth With Fuel Trim"; newCsvHeaders[indexOfCalculatedAFR] = "Calculated Commanded AFR"; newCsvHeaders[indexOfCalculatedLoad] = "Calculated Load"; //get stoich double stoichAFR; if (!fordPCMHelper.TryGetDouble(2300, out stoichAFR)) { MessageBox.Show("Couldn't read Stoich AFR value (2300), we will assume 14.64", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); stoichAFR = 14.64; } Boolean calculatePulseWidth = true; //get low slope double lowSlope; if (!fordPCMHelper.TryGetDouble(12010, out lowSlope)) { MessageBox.Show("Couldn't read Injector Low Slope (12010), we will not be able to calculate pulse width!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); calculatePulseWidth = false; } lowSlope /= 3600.0; //get high slope double highSlope; if (!fordPCMHelper.TryGetDouble(12011, out highSlope)) { MessageBox.Show("Couldn't read Injector High Slope (12010), we will not be able to calculate pulse width!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); calculatePulseWidth = false; } highSlope /= 3600.0; //get offset double breakpoint; if (!fordPCMHelper.TryGetDouble(12012, out breakpoint)) { MessageBox.Show("Couldn't read Injector breakpoint (12012), we will not be able to calculate pulse width!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); calculatePulseWidth = false; } //get offset double offset; if (!fordPCMHelper.TryGetDouble(32050, out offset)) { MessageBox.Show("Couldn't read Injector offset at 12.0V (32050), we will not be able to calculate pulse width!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); //calculatePulseWidth = false; offset = 0.00146499997936189; } double displacement; if (!fordPCMHelper.TryGetDouble(50000, out displacement)) { MessageBox.Show("Couldn't read engine displacement (50000), we will assume 4.0L (0.00172 lb)", "Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); displacement = 0.00172; } Boolean lambdaErrorIssued = false; int samplesProcessed = 0; int totalCount = csvData.Length; Parallel.For(0, totalCount, i => { if (failed) { return; } double camAngle = csvData[i][indexOfCamAngle]; double rpm = csvData[i][indexOfRPM]; double map = csvData[i][indexOfMAP]; double csvPulseWidthSeconds = csvData[i][indexOfPulse]; double fuelTrim = csvData[i][indexOfFuelTrim]; double TPS = -1.0; double commandedLambda = 1.0; //Get the commanded lambda value if (indexOfTPS != -1) { TPS = csvData[i][indexOfTPS]; TPS *= 1.25; if (!fordPCMHelper.TryGetTableValue(rpm, TPS, 50151, out commandedLambda)) { if (!lambdaErrorIssued) { MessageBox.Show(String.Format("Failed to get commanded lambda for {0}rpm {1}TPS%, we will use 1.0 from now on and no longer report anymore errors of this type", rpm, TPS)); } lambdaErrorIssued = true; commandedLambda = 1.0; } } newCsvData[i][indexOfCalculatedAFR] = commandedLambda; //interpolate the map per airmass values double mapPerAirmass; double mapPerZeroAirmass; if (!fordPCMHelper.TryGetTableValue(rpm, camAngle, 50051, out mapPerZeroAirmass)) { MessageBox.Show(String.Format("Failed to get Map per Zero Airmass for {0}rpm {1} Cam Angle, this is a fatal error, giving up, sorry bro", rpm, camAngle)); failed = true; return; } if (!fordPCMHelper.TryGetTableValue(rpm, camAngle, 50055, out mapPerAirmass)) { MessageBox.Show(String.Format("Failed to get Map per Airmass for {0}rpm {1} Cam Angle, this is a fatal error, giving up, sorry bro", rpm, camAngle)); failed = true; return; } newCsvData[i][indexOfMapPerAirmass] = mapPerAirmass; newCsvData[i][indexOfMapPerZeroAirmass] = mapPerZeroAirmass; //Calculate the airmass double airMassLbs = (map - mapPerZeroAirmass) / mapPerAirmass; newCsvData[i][indexOfAirMass] = airMassLbs; //Calculate the load newCsvData[i][indexOfCalculatedLoad] = airMassLbs / displacement; //Calculate the fuelmass via the SD maps double fuelMassViaSD = airMassLbs / (stoichAFR *commandedLambda); newCsvData[i][indexOfFuelMassViaSD] = fuelMassViaSD; //Calculate the pulse width double calculatedPulseWidthSec; //When below the breakpoint if (fuelMassViaSD <= breakpoint) { calculatedPulseWidthSec = (fuelMassViaSD / lowSlope) + offset; } else { //fuelmass=high*(ms-(breakpoint*((1/low)-(1/high))+offset)) //ms=(breakpoint*((1/low)-(1/high))+(fuelmass/highslope)+offset calculatedPulseWidthSec = (breakpoint * ((1 / lowSlope) - (1 / highSlope))) + (fuelMassViaSD / highSlope) + offset; } double calculatedPulseWithMs = (calculatedPulseWidthSec - offset) * 1000.0; //This is the low cut off if (calculatedPulseWithMs < 0.2) { calculatedPulseWithMs = 0.2; } double csvPulseWidthMs = csvPulseWidthSeconds * 1000.0; newCsvData[i][indexOfCalculatedInjectorPW] = calculatedPulseWithMs; double trimmedInjPulse = calculatedPulseWithMs *fuelTrim; newCsvData[i][indexOfCalculatedInjectorPWTrimmed] = trimmedInjPulse; //Calculate the fuel mass via a reverse calc of the injector pulse width double breakpointMs = ((breakpoint / lowSlope) + offset) * 1000.0; double fuelMassViaInjectorMs; if (csvPulseWidthMs < breakpointMs) { fuelMassViaInjectorMs = lowSlope * (csvPulseWidthSeconds); } else { fuelMassViaInjectorMs = highSlope * (csvPulseWidthSeconds - (breakpoint * ((1 / lowSlope) - (1 / highSlope)))); } newCsvData[i][indexOfFuelMassViaInjMs] = fuelMassViaInjectorMs; newCsvData[i][indexOfPulse] = newCsvData[i][indexOfPulse] * 1000.0; samplesProcessed++; if (samplesProcessed % 120 == 0) { progress.Report((samplesProcessed * 100 / totalCount)); } }); if (failed) { return(null); } //Calculate the R² value double errorOfCalculatedInjPulse = GetRSquared(newCsvData, indexOfCalculatedInjectorPW, indexOfPulse, 1.0, 1000.0); double errorOfCalculatedInjPulseTrimmed = GetRSquared(newCsvData, indexOfCalculatedInjectorPWTrimmed, indexOfPulse, 1.0, 1000.0); double errorOfCalculatedFuelMassPulse = GetRSquared(newCsvData, indexOfFuelMassViaSD, indexOfFuelMassViaInjMs, 1.0, 1.0); csvHeaders = newCsvHeaders; csvData = newCsvData; HelperMethods.WriteCSV(@"C:\temp\test.csv", csvHeaders, csvData); returnArray[0] = errorOfCalculatedInjPulse; returnArray[1] = errorOfCalculatedInjPulseTrimmed; returnArray[2] = errorOfCalculatedFuelMassPulse; return(returnArray); }); if (failed) { return; } if (HelperMethods.IsValidDouble(results[0])) { calculatedInjectorPulseErrorTextBox.Text = String.Format("{0:0.###}", results[0]); } if (HelperMethods.IsValidDouble(results[1])) { calculatedInjectorTrimmedPulseErrorTextBox.Text = String.Format("{0:0.###}", results[1]); } if (HelperMethods.IsValidDouble(results[2])) { calculatedFuelMassErrorTextBox.Text = String.Format("{0:0.###}", results[2]); } }
static public double GetRSquared(double[][] array1, int index1, int index2, double scale1, double scale2) { double R = 0; try { // sum(xy) int invalidEntries = 0; double sumXY = 0; for (int c = 0; c <= array1.Length - 1; c++) { double number1 = (array1[c][index1] * scale1); double number2 = (array1[c][index2] * scale2); if (!HelperMethods.IsValidDouble(number1) || !HelperMethods.IsValidDouble(number2)) { invalidEntries++; continue; } sumXY = sumXY + (array1[c][index1] * scale1) * (array1[c][index2] * scale2); } // sum(x) double sumX = 0; for (int c = 0; c <= array1.Length - 1; c++) { sumX = sumX + (array1[c][index1] * scale1); } // sum(y) double sumY = 0; for (int c = 0; c <= array1.Length - 1; c++) { sumY = sumY + (array1[c][index2] * scale2); } // sum(x^2) double sumXX = 0; for (int c = 0; c <= array1.Length - 1; c++) { sumXX = sumXX + (array1[c][index1] * scale1) * (array1[c][index1] * scale1); } // sum(y^2) double sumYY = 0; for (int c = 0; c <= array1.Length - 1; c++) { sumYY = sumYY + (array1[c][index2] * scale2) * (array1[c][index2] * scale2); } // n int n = array1.Length - invalidEntries; R = (n * sumXY - sumX * sumY) / (Math.Pow((n * sumXX - Math.Pow(sumX, 2)), 0.5) * Math.Pow((n * sumYY - Math.Pow(sumY, 2)), 0.5)); } catch (Exception ex) { throw (ex); } return(R * R); }
public void PopulateMainGridView(string xHeader, string yHeader, string zHeader, string vHeader, bool exactMatch = false) { SecondaryGridView.Rows.Clear(); SecondaryGridView.Columns.Clear(); MainGridView.Rows.Clear(); if (MainXValuesAreNullOrEmpty()) { UpdateMainXAxis(ScaleForm.GenerateAxis(250.0, 6000.0, 250.0)); } if (MainYValuesAreNullOrEmpty()) { UpdateMainYAxis(ScaleForm.GenerateAxis(-10.0, 45.0, 10.0)); } int i = 0; int xIndex = -1; int yIndex = -1; int zIndex = -1; int uIndex = -1; int numberOfHeaders = csvData[0].Length; for (i = 0; i < numberOfHeaders; i++) { if (exactMatch) { if (csvHeaders[i].Equals(xHeader)) { xIndex = i; } if (csvHeaders[i].Equals(yHeader)) { yIndex = i; } if (csvHeaders[i].Equals(zHeader)) { zIndex = i; } if (csvHeaders[i].Equals(vHeader)) { uIndex = i; } } else { if (csvHeaders[i].Contains(xHeader)) { xIndex = i; } if (csvHeaders[i].Contains(yHeader)) { yIndex = i; } if (csvHeaders[i].Contains(zHeader)) { zIndex = i; } if (csvHeaders[i].Contains(vHeader)) { uIndex = i; } } if (yIndex != -1 && xIndex != -1 && zIndex != -1 && uIndex != -1) { break; } } //We couldn't find the indexs if (yIndex == -1 || xIndex == -1 || zIndex == -1 || uIndex == -1) { MessageBox.Show("Error: Couldn't find X/Y/Z index in file", "Error"); return; } int height = csvData[0].Length; MainGridViewData = new List <double[]> [MainGridViewxValues.Length, MainGridViewyValues.Length]; for (i = 0; i < MainGridView.Columns.Count; i++) { for (int j = 0; j < MainGridViewyValues.Length; j++) { MainGridViewData[i, j] = new List <double[]>(); } } double uMult = 1.0; double yMult = 1.0; double xMult = 1.0; double zMult = 1.0; if (!double.TryParse(UAxisMultiplierTextBox.Text, out uMult)) { uMult = 1.0; } if (!double.TryParse(YAxisMultiplierTextBox.Text, out yMult)) { yMult = 1.0; } if (!double.TryParse(XAxisMultiplierTextBox.Text, out xMult)) { xMult = 1.0; } if (!double.TryParse(ZAxisMultiplierTextBox.Text, out zMult)) { zMult = 1.0; } double[][] csvDataCopy = csvData.Select(a => a.ToArray()).ToArray(); double xsdignore, ysdignore, zsdignore, usdignore; if (sdFiltercheckBox.Checked == true) { if (double.TryParse(xsd.Text, out xsdignore)) { RemoveOutliers(ref csvDataCopy, xIndex, xsdignore); } if (double.TryParse(ysd.Text, out ysdignore)) { RemoveOutliers(ref csvDataCopy, xIndex, ysdignore); } if (double.TryParse(zsd.Text, out zsdignore)) { RemoveOutliers(ref csvDataCopy, zIndex, zsdignore); } if (double.TryParse(usd.Text, out usdignore)) { RemoveOutliers(ref csvDataCopy, uIndex, usdignore); } } //Iterate each time sample and add it to the appropriate x,y list int numberOfSamples = csvDataCopy.Length; double minZ = double.MaxValue; double range = 0.0; for (i = 0; i < numberOfSamples; i++) { double x = csvDataCopy[i][xIndex] * xMult; double y = csvDataCopy[i][yIndex] * yMult; double z = csvDataCopy[i][zIndex] * zMult; double u = csvDataCopy[i][uIndex] * uMult; //Skip the entry if it is invalid if (!HelperMethods.IsValidDouble(x) || !HelperMethods.IsValidDouble(y) || !HelperMethods.IsValidDouble(z)) { continue; } int[] coordinates = GetNearestCoordinate(x, y, MainGridViewxValues, MainGridViewyValues); double[] cell = new double[2]; cell[0] = z; cell[1] = u; MainGridViewData[coordinates[0], coordinates[1]].Add(cell); } //Do not display cells with a count below this level double ignoreLowerLimitValue; if (!double.TryParse(ignoreLowerLimit.Text, out ignoreLowerLimitValue)) { ignoreLowerLimitValue = Double.MinValue; } double ignoreUpperLimitValue; if (!double.TryParse(ignoreUpperLimit.Text, out ignoreUpperLimitValue)) { ignoreUpperLimitValue = Double.MaxValue; } double ignoreCellCount; if (!double.TryParse(cellCountIgnoreTextBox.Text, out ignoreCellCount)) { ignoreCellCount = 0.0; } for (i = 0; i < MainGridViewyValues.Length; i++) { DataGridViewRow row = new DataGridViewRow(); row.HeaderCell = new DataGridViewRowHeaderCell(); row.HeaderCell.Value = MainGridViewyValues[i].ToString(); for (int j = 0; j < MainGridViewxValues.Length; j++) { double[] primaryArray = new double[MainGridViewData[j, i].Count]; double[] secondaryArray = new double[MainGridViewData[j, i].Count]; for (int k = 0; k < primaryArray.Length; k++) { primaryArray[k] = MainGridViewData[j, i][k][0]; secondaryArray[k] = MainGridViewData[j, i][k][1]; } if (primaryArray.Length < ignoreCellCount) { primaryArray = new double[0]; } DataGridViewTuningCell cell = new DataGridViewTuningCell(secondaryArray, primaryArray, range, minZ, MainGridView.currentMode, ignoreLowerLimitValue, ignoreUpperLimitValue); row.Cells.Add(cell); } MainGridView.Rows.Add(row); } MainGridView.RowHeadersWidth = 60; MainGridView.SetFormat(MainGridView.currentMode, true); }