예제 #1
0
        //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);
        }
예제 #2
0
        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;
            }
        }
예제 #3
0
        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);
        }
예제 #4
0
        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;
            }
        }
예제 #5
0
        //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]);
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
        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);
        }