Drivers drivers = new Drivers(); // Need driver objects for the setup of targets public WinFilters() { Targets targets = new Targets(); FilterSPL.Initialize(); InitializeComponent(); Version version = Assembly.GetExecutingAssembly().GetName().Version; string assemblyName = Assembly.GetExecutingAssembly().GetName().Name; this.Text = string.Format("{0} Version {1}.{2}.{3} Revision {4} - Windows Acoustic Filter Graphing Tool", assemblyName, version.Major, version.Minor, version.Build, version.Revision); systemGraph.GraphPane.Title.Text = "Magnitude and Phase"; InitGraph.SetZedgraphFormat(systemGraph); // Remove the default option from all graphs systemGraph.ContextMenuBuilder += new ZedGraphControl.ContextMenuBuilderEventHandler(WinFilters.ContextMenuRemoveDefault); // Add the save options to the specific graphs systemGraph.ContextMenuBuilder += new ZedGraphControl.ContextMenuBuilderEventHandler(WinFilters.ContextMenuSaveLPFilterWfr); // Add the save options to the specific graphs systemGraph.ContextMenuBuilder += new ZedGraphControl.ContextMenuBuilderEventHandler(WinFilters.ContextMenuSaveHPFilterTwtr); ExportSPL.Instance.nominal = Convert.ToDouble(splNumeric.Value); Refresh(); // Refresh to paint the graph components // Create the frequency range array for use throughout the program. // frequencies.InitFrequencyRange(); }
/// <summary> /// Calculates the response for a theoretical filter. These are used for design acoustic filter targets, not electrical. /// </summary> /// <param name="driver"></param> /// <param name="frequencyRange"></param> /// <param name="phaseAtFc"></param> /// <param name="hpIsInverted"></param> /// <returns>besselFactoredFc, but only if it is for a Bessel.</returns> public static void Calculate(DriverCore.DriverCore driver, double[] frequencyRange , double FcLP, out double splAtFcLP, out double phaseAtFcLP , double FcHP, out double splAtFcHP, out double phaseAtFcHP , out double factoredFcLP, out double factoredFcHP , bool hpIsInverted = false, double offsetMm = 0.0, bool lpIsOffset = false, bool hpIsOffset = false) { double gain = 1; // Epsilon - This allows change in gain later double minInterpFreq = frequencyRange[0]; int rangeUpperLimit = frequencyRange.Count() - 1; // Count of the frequency specified range double maxInterpFreq = frequencyRange[rangeUpperLimit]; // Upper frequency limit of the interpolations to be made double interpFreq; // Used for iterations double[,] interpMagPhaseLP = new double[frequencyRange.Count(), 3]; // LP mag and phase. double[,] interpMagPhaseHP = new double[frequencyRange.Count(), 3]; // HP mag and phase. double[,] interpMagPhase = new double[frequencyRange.Count(), 3]; // Array to hold interp mag and phase. double gainLP = 1.0; double gainHP = 1.0; double gainAtFcLP = 0.0; double gainAtFcHP = 0.0; double phaseLP = 0.0; double phaseHP = 0.0; // These are calculated on each loop, but it was needed to be able to locate the specific set of two // frequencies between which the Fc is located. splAtFcLP = 0.0; splAtFcHP = 0.0; phaseAtFcLP = 0.0; phaseAtFcHP = 0.0; factoredFcLP = 1.0; factoredFcHP = 1.0; for (int i = 0; i < rangeUpperLimit; i++) { interpFreq = frequencyRange[i]; // Interpolation frequency points to use from the range specified. interpMagPhase[i, 0] = interpFreq; interpMagPhase[i, 1] = 1; // SPL default interpMagPhase[i, 2] = 0; // Phase default interpMagPhaseLP[i, 0] = interpMagPhase[i, 0]; interpMagPhaseLP[i, 1] = interpMagPhase[i, 1]; interpMagPhaseLP[i, 2] = interpMagPhase[i, 2]; interpMagPhaseHP[i, 0] = interpMagPhase[i, 0]; interpMagPhaseHP[i, 1] = interpMagPhase[i, 1]; interpMagPhaseHP[i, 2] = interpMagPhase[i, 2]; double s; double besselFactor = 1.0; // Multiply the Fc (divide into s) // Add offset to LP or HP based on user input double offsetDelay = (2 * Math.PI * interpMagPhase[i, 0] * (offsetMm / 344000)); double offsetDelayAtFcLP = (2 * Math.PI * FcLP * (offsetMm / 344000)); double offsetDelayAtFcHP = (2 * Math.PI * FcHP * (offsetMm / 344000)); double offsetLP = 0, offsetHP = 0; double offsetLpAtFc = 0, offsetHpAtFc = 0; if (lpIsOffset) { offsetLP = offsetDelay; offsetLpAtFc = offsetDelayAtFcLP; } if (hpIsOffset) { offsetHP = offsetDelay; offsetHpAtFc = offsetDelayAtFcHP; } driver.target.splInterpolated[i, 1] = 0; driver.target.splInterpolated[i, 2] = 0; if ((driver.target.section == FilterCore.Section.LP) || (driver.target.section == FilterCore.Section.BP)) { s = interpFreq / driver.target.frequencyLP; switch (driver.target.nameLP) { case AcousticTargets.FilterName.Butterworth: { factoredFcLP = driver.target.frequencyLP; gainLP = FilterSPL.ButterworthSPL(driver.target, driver.target.nameLP, driver.target.orderLP, gain, s); phaseLP = FilterPhase.ButterworthPhase(driver.target.orderLP, s) + offsetLP; phaseLP = FilterCore.RotatePhase(phaseLP); phaseAtFcLP = FilterPhase.ButterworthPhase(driver.target.orderLP, s = 1) + offsetLpAtFc; phaseAtFcLP = (FilterCore.RotatePhase(phaseAtFcLP) * 180.0) / Math.PI; gainAtFcLP = FilterSPL.ButterworthSPL(driver.target, driver.target.nameLP, driver.target.orderLP, gain, s = 1); break; } case AcousticTargets.FilterName.LinkwitzRiley: { if (driver.target.fullTypeLP == null) { return; } // Handle odd-order values not permissible for L-R factoredFcLP = driver.target.frequencyLP; gainLP = Math.Pow(FilterSPL.ButterworthSPL(driver.target, driver.target.nameLP, driver.target.orderLP, gain, s), 2); phaseLP = FilterPhase.LinkwitzRileyPhase(driver.target.orderLP, s) + offsetLP; phaseLP = FilterCore.RotatePhase(phaseLP); phaseAtFcLP = FilterPhase.LinkwitzRileyPhase(driver.target.orderLP, s = 1) + offsetLpAtFc; phaseAtFcLP = FilterCore.RotatePhase(phaseAtFcLP) * 180 / Math.PI; gainAtFcLP = Math.Pow(FilterSPL.ButterworthSPL(driver.target, driver.target.nameLP, driver.target.orderLP, gain, s = 1), 2); break; } case AcousticTargets.FilterName.Bessel: { factoredFcLP = driver.target.frequencyLP; gainLP = FilterSPL.BesselSPL(driver.target.orderLP, gain, s); phaseLP = FilterPhase.BesselPhase(driver.target.orderLP, s) + offsetLP; phaseLP = FilterCore.RotatePhase(phaseLP); phaseAtFcLP = FilterPhase.BesselPhase(driver.target.orderLP, s = 1) + offsetLpAtFc; phaseAtFcLP = FilterCore.RotatePhase(phaseAtFcLP) * 180 / Math.PI; gainAtFcLP = FilterSPL.BesselSPL(driver.target.orderLP, gain, s = 1); break; } case AcousticTargets.FilterName.BesselPhaseMatch: { switch (driver.target.orderLP) { case 2: { besselFactor = 0.58; break; } case 3: { besselFactor = 1.0; break; } case 4: { besselFactor = 0.31; break; } } s /= besselFactor; factoredFcLP = driver.target.frequencyLP * besselFactor; gainLP = FilterSPL.BesselSPL(driver.target.orderLP, gain, s); phaseLP = FilterPhase.BesselPhase(driver.target.orderLP, s) + offsetLP; phaseLP = FilterCore.RotatePhase(phaseLP); phaseAtFcLP = FilterPhase.BesselPhase(driver.target.orderLP, s = 1 / besselFactor) + offsetLpAtFc; phaseAtFcLP = FilterCore.RotatePhase(phaseAtFcLP) * 180 / Math.PI; gainAtFcLP = FilterSPL.BesselSPL(driver.target.orderLP, gain, s = 1 / besselFactor); break; } case AcousticTargets.FilterName.BesselFlattest: { switch (driver.target.orderLP) { case 2: { besselFactor = 0.51; break; } case 3: { besselFactor = 1.0; break; } case 4: { besselFactor = 0.40; break; } } s /= besselFactor; factoredFcLP = driver.target.frequencyLP * besselFactor; gainLP = FilterSPL.BesselSPL(driver.target.orderLP, gain, s); phaseLP = FilterPhase.BesselPhase(driver.target.orderLP, s) + offsetLP; phaseLP = FilterCore.RotatePhase(phaseLP); phaseAtFcLP = FilterPhase.BesselPhase(driver.target.orderLP, s = 1 / besselFactor) + offsetLpAtFc; phaseAtFcLP = FilterCore.RotatePhase(phaseAtFcLP) * 180 / Math.PI; gainAtFcLP = FilterSPL.BesselSPL(driver.target.orderLP, gain, s = 1 / besselFactor); break; } case AcousticTargets.FilterName.Bessel3db: { switch (driver.target.orderLP) { case 2: { besselFactor = 0.73; break; } case 3: { besselFactor = 1.0; break; } case 4: { besselFactor = 0.48; break; } } s /= besselFactor; factoredFcLP = driver.target.frequencyLP * besselFactor; gainLP = FilterSPL.BesselSPL(driver.target.orderLP, gain, s); phaseLP = FilterPhase.BesselPhase(driver.target.orderLP, s) + offsetLP; phaseLP = FilterCore.RotatePhase(phaseLP); phaseAtFcLP = FilterPhase.BesselPhase(driver.target.orderLP, s = 1 / besselFactor) + offsetLpAtFc; phaseAtFcLP = FilterCore.RotatePhase(phaseAtFcLP) * 180 / Math.PI; gainAtFcLP = FilterSPL.BesselSPL(driver.target.orderLP, gain, s = 1 / besselFactor); break; } case AcousticTargets.FilterName.None: { break; } default: { MessageBox.Show("Section.LP (.typeLP) is not correct."); break; } } } if ((driver.target.section == FilterCore.Section.HP) || (driver.target.section == FilterCore.Section.BP)) { s = interpFreq / driver.target.frequencyHP; // f/Fc switch (driver.target.nameHP) { case AcousticTargets.FilterName.Butterworth: { factoredFcHP = driver.target.frequencyHP; gainHP = FilterSPL.ButterworthSPL(driver.target, driver.target.nameHP, driver.target.orderHP, gain, 1 / s); phaseHP = FilterPhase.ButterworthPhase(driver.target.orderHP, s, true) + offsetHP; offsetHpAtFc *= factoredFcHP; // Determine the offset using the factored Fc. phaseAtFcHP = FilterPhase.ButterworthPhase(driver.target.orderHP, s = 1, true) + offsetHpAtFc; if (hpIsInverted) { phaseHP = FilterCore.InvertPhase(phaseHP); phaseAtFcHP = FilterCore.InvertPhase(phaseAtFcHP) * 180 / Math.PI; } else { phaseHP = FilterCore.RotatePhase(phaseHP); phaseAtFcHP = FilterCore.RotatePhase(phaseAtFcHP) * 180 / Math.PI; } gainAtFcHP = FilterSPL.ButterworthSPL(driver.target, driver.target.nameHP, driver.target.orderHP, gain, s = 1); break; } case AcousticTargets.FilterName.LinkwitzRiley: { if (driver.target.fullTypeHP == null) { return; } // Handle odd-order values not permissible for L-R factoredFcHP = driver.target.frequencyHP; if (null == driver.target.fullTypeHP) { return; } // Handle odd-order values not permissible for L-R gainHP = Math.Pow(FilterSPL.ButterworthSPL(driver.target, driver.target.nameHP, driver.target.orderHP, gain, 1 / s), 2); phaseHP = FilterPhase.LinkwitzRileyPhase(driver.target.orderHP, s, true) + offsetHP; offsetHpAtFc *= factoredFcHP; // Determine the offset using the factored Fc. phaseAtFcHP = FilterPhase.LinkwitzRileyPhase(driver.target.orderHP, s = 1, true) + offsetHpAtFc; if (hpIsInverted) { phaseHP = FilterCore.InvertPhase(phaseHP); phaseAtFcHP = FilterCore.InvertPhase(phaseAtFcHP) * 180 / Math.PI; } else { phaseHP = FilterCore.RotatePhase(phaseHP); phaseAtFcHP = FilterCore.RotatePhase(phaseAtFcHP) * 180 / Math.PI; } gainAtFcHP = Math.Pow(FilterSPL.ButterworthSPL(driver.target, driver.target.nameHP, driver.target.orderHP, gain, s = 1), 2); break; } case AcousticTargets.FilterName.Bessel: { factoredFcHP = driver.target.frequencyHP; gainHP = FilterSPL.BesselSPL(driver.target.orderHP, gain, 1 / s); phaseHP = FilterPhase.BesselPhase(driver.target.orderHP, s, true) + offsetHP; offsetHpAtFc *= factoredFcHP; // Determine the offset using the factored Fc. phaseAtFcHP = FilterPhase.BesselPhase(driver.target.orderHP, s = 1, true) + offsetHpAtFc; if (hpIsInverted) { phaseHP = FilterCore.InvertPhase(phaseHP); phaseAtFcHP = FilterCore.InvertPhase(phaseAtFcHP) * 180 / Math.PI; } else { phaseHP = FilterCore.RotatePhase(phaseHP); phaseAtFcHP = FilterCore.RotatePhase(phaseAtFcHP) * 180 / Math.PI; } gainAtFcHP = FilterSPL.BesselSPL(driver.target.orderHP, gain, s = 1); break; } case AcousticTargets.FilterName.BesselPhaseMatch: { switch (driver.target.orderHP) { case 2: { besselFactor = 1 / 0.58; break; } case 3: { besselFactor = 1.0; break; } case 4: { besselFactor = 1 / 0.31; break; } } s /= besselFactor; factoredFcHP = driver.target.frequencyHP * besselFactor; gainHP = FilterSPL.BesselSPL(driver.target.orderHP, gain, 1 / s); phaseHP = FilterPhase.BesselPhase(driver.target.orderHP, s, true) + offsetHP; offsetHpAtFc *= factoredFcHP; // Determine the offset using the factored Fc. phaseAtFcHP = FilterPhase.BesselPhase(driver.target.orderHP, s = 1 / besselFactor, true) + offsetHpAtFc; if (hpIsInverted) { phaseHP = FilterCore.InvertPhase(phaseHP); phaseAtFcHP = FilterCore.InvertPhase(phaseAtFcHP) * 180 / Math.PI; } else { phaseHP = FilterCore.RotatePhase(phaseHP); phaseAtFcHP = FilterCore.RotatePhase(phaseAtFcHP) * 180 / Math.PI; } gainAtFcHP = FilterSPL.BesselSPL(driver.target.orderHP, gain, s = 1 * besselFactor); break; } case AcousticTargets.FilterName.BesselFlattest: { switch (driver.target.orderHP) { case 2: { besselFactor = 1 / 0.51; break; } case 3: { besselFactor = 1.0; break; } case 4: { besselFactor = 1 / 0.40; break; } } s /= besselFactor; factoredFcHP = driver.target.frequencyHP * besselFactor; gainHP = FilterSPL.BesselSPL(driver.target.orderHP, gain, 1 / s); phaseHP = FilterPhase.BesselPhase(driver.target.orderHP, s, true) + offsetHP; offsetHpAtFc *= factoredFcHP; // Determine the offset using the factored Fc. phaseAtFcHP = FilterPhase.BesselPhase(driver.target.orderHP, s = 1 / besselFactor, true) + offsetHpAtFc; if (hpIsInverted) { phaseHP = FilterCore.InvertPhase(phaseHP); phaseAtFcHP = FilterCore.InvertPhase(phaseAtFcHP) * 180 / Math.PI; } else { phaseHP = FilterCore.RotatePhase(phaseHP); phaseAtFcHP = FilterCore.RotatePhase(phaseAtFcHP) * 180 / Math.PI; } gainAtFcHP = FilterSPL.BesselSPL(driver.target.orderHP, gain, s = 1 * besselFactor); break; } case AcousticTargets.FilterName.Bessel3db: { switch (driver.target.orderHP) { case 2: { besselFactor = 1 / 0.73; break; } case 3: { besselFactor = 1.0; break; } case 4: { besselFactor = 1 / 0.48; break; } } s /= besselFactor; factoredFcHP = driver.target.frequencyHP * besselFactor; gainHP = FilterSPL.BesselSPL(driver.target.orderHP, gain, 1 / s); phaseHP = FilterPhase.BesselPhase(driver.target.orderHP, s, true) + offsetHP; offsetHpAtFc *= factoredFcHP; // Determine the offset using the factored Fc. phaseAtFcHP = FilterPhase.BesselPhase(driver.target.orderHP, s = 1 / besselFactor, true) + offsetHpAtFc; if (hpIsInverted) { phaseHP = FilterCore.InvertPhase(phaseHP); phaseAtFcHP = FilterCore.InvertPhase(phaseAtFcHP) * 180 / Math.PI; } else { phaseHP = FilterCore.RotatePhase(phaseHP); phaseAtFcHP = FilterCore.RotatePhase(phaseAtFcHP) * 180 / Math.PI; } gainAtFcHP = FilterSPL.BesselSPL(driver.target.orderHP, gain, s = 1 * besselFactor); break; } case AcousticTargets.FilterName.None: { break; } default: { MessageBox.Show("Section.HP (.typeHP) is not correct. Must be null."); break; } } } double factorLP = 0.0; double factorFcLP = 0.0; Complex complexLP = 0.0; double factorHP = 0.0; double factorFcHP = 0.0; Complex complexHP = 0.0; interpMagPhase[i, 0] = interpFreq; switch (driver.target.section) { case FilterCore.Section.LP: { factorLP = 20 * Math.Log10(gainLP); interpMagPhaseLP[i, 1] = driver.target.targetMag + factorLP; // Subtract filter factorLP (in db) from maximum gain (sensitivity) interpMagPhaseLP[i, 2] = phaseLP; complexLP = Complex.FromPolarCoordinates(factorLP, phaseLP); factorFcLP = 20 * Math.Log10(gainAtFcLP); splAtFcLP = driver.target.targetMag + factorFcLP; interpMagPhase[i, 1] = interpMagPhaseLP[i, 1]; interpMagPhase[i, 2] = interpMagPhaseLP[i, 2] * 180 / Math.PI; break; } case FilterCore.Section.HP: { factorHP = 20 * Math.Log10(gainHP); interpMagPhaseHP[i, 1] = driver.target.targetMag + factorHP; // Subtract filter factorHP (in db) from maximum gain (sensitivity) interpMagPhaseHP[i, 2] = phaseHP; complexHP = Complex.FromPolarCoordinates(factorHP, phaseHP); factorFcHP = 20 * Math.Log10(gainAtFcHP); splAtFcHP = driver.target.targetMag + factorFcHP; interpMagPhase[i, 1] = interpMagPhaseHP[i, 1]; interpMagPhase[i, 2] = interpMagPhaseHP[i, 2] * 180 / Math.PI; break; } case FilterCore.Section.BP: { // Update the out arguments factorFcLP = 20 * Math.Log10(gainAtFcLP); factorFcHP = 20 * Math.Log10(gainAtFcHP); splAtFcLP = driver.target.targetMag + factorFcLP; splAtFcHP = driver.target.targetMag + factorFcHP; // Calculate the bandpass //Complex target = Complex.FromPolarCoordinates(driver.target.targetMag, 0.0); Complex complexGainLP = Complex.FromPolarCoordinates(gainLP, phaseLP); Complex complexGainHP = Complex.FromPolarCoordinates(gainHP, phaseHP); Complex complexGainSum = complexGainLP * complexGainHP; double totalGainDb = driver.target.targetMag + (20 * Math.Log10(complexGainSum.Magnitude)); interpMagPhase[i, 1] = totalGainDb; interpMagPhase[i, 2] = complexGainSum.Phase * 180 / Math.PI; break; } } } driver.target.splInterpolated = interpMagPhase; return; }