public FourierSeries(IEnumerable<double> samples) { _complexes = samples.Select(x => new Complex(x, 0)) .ToArray(); var dft = new MathNet.Numerics.IntegralTransforms.Algorithms.DiscreteFourierTransform(); dft.BluesteinForward(_complexes, FourierOptions.NoScaling); }
/// <summary> /// Perform a IFFT using Bluestein's algorithm /// </summary> /// <param name="input">Array of complex numbers</param> /// <returns></returns> public Complex[] IFFT(Complex[] input) { Complex[] output = new Complex[input.Length]; Array.Copy(input, output, input.Length); var transform = new MN.Algorithms.DiscreteFourierTransform(); transform.BluesteinInverse(output, MN.FourierOptions.Matlab); return output; }
/// <summary> /// Set the channel properties /// </summary> /// <param name="channel"></param> public static void SetChannel(Dict channel) { // validate channel's properties var data = new Complex[BlocksPerSymbol]; foreach(var curr in channel.Keys()) { int index; double value; if (!int.TryParse(curr, out index)) throw new Exception(String.Format("Channel quality indexes must be integers. '{0}' is not a valid integer!", curr)); if (index >= data.Length) throw new Exception("Key exceeds array length"); if (!double.TryParse(channel.Get(curr), out value)) throw new Exception(String.Format("Channel quality values must be floats. '{0}' is not a valid float!", curr)); data[index] = new Complex(value, 0); } // reverse-transform to get SNRs var fft = new MathNet.Numerics.IntegralTransforms.Algorithms.DiscreteFourierTransform(); fft.BluesteinInverse(data, MathNet.Numerics.IntegralTransforms.FourierOptions.Default); ChannelQuality = data.Select(p => p.Magnitude).ToArray(); }
public TraceFeatures(ICollection <Touch> touches) { var trace = touches.Where(t => t.FingerId == touches.First().FingerId); long startTime = trace.First().Time; List <double> timePoints = trace.Select(t => (double)(t.Time - startTime)).ToList(); List <double> xValues = trace.Select(t => (double)t.X).ToList(); List <double> yValues = trace.Select(t => (double)t.Y).ToList(); var X = new double[trace.Count()]; var Y = new double[trace.Count()]; Time = new int[trace.Count()]; var ind = 0; foreach (var touch in trace) { X[ind] = (double)touch.X; Y[ind] = (double)touch.Y; Time[ind] = (int)(touch.Time - startTime); ind++; } var isNotDistinct = timePoints.GroupBy(n => n).Any(c => c.Count() > 1); if (isNotDistinct) { return; } //do interpolation stuff var splineX = new MathNet.Numerics.Interpolation.Algorithms.AkimaSplineInterpolation(timePoints, xValues); var splineY = new MathNet.Numerics.Interpolation.Algorithms.AkimaSplineInterpolation(timePoints, yValues); var fft = new MathNet.Numerics.IntegralTransforms.Algorithms.DiscreteFourierTransform(); //fft.BluesteinForward() //var splineX = new MathNet.Numerics.Interpolation.Algorithms.CubicSplineInterpolation(timePoints, xValues); //var splineY = new MathNet.Numerics.Interpolation.Algorithms.CubicSplineInterpolation(timePoints, yValues); /* * int nSteps = (int)timePoints.Last() / 10; * * var XInt = new double[nSteps]; * var YInt = new double[nSteps]; * var vX = new double[nSteps]; * var vY = new double[nSteps]; * TimeInterpolated = new int[nSteps]; * //var aX = new double[nSteps]; * //var aY = new double[nSteps]; * * * for (int i = 0; i < nSteps; i++) * { * XInt[i] = splineX.Interpolate(i * 10); * YInt[i] = splineY.Interpolate(i * 10); * vX[i] = splineX.Differentiate(i * 10); * vY[i] = splineY.Differentiate(i * 10); * TimeInterpolated[i] = (int)(i * 10); * } */ /* * //init arrays * int n = touches.Count; * var dT = new int?[n]; * var X = new decimal[n]; * var Y = new decimal[n]; * var dX = new decimal?[n]; * var dY = new decimal?[n]; * var ddX = new decimal?[n]; * var ddY = new decimal?[n]; * var StartDistance = new decimal[n]; * var StartDirection = new decimal[n]; * var Direction = new decimal?[n]; * var Curvature = new decimal?[n]; * var CurvX = new decimal?[n]; * var CurvY = new decimal?[n]; * var DirX = new decimal?[n]; * var DirY = new decimal?[n]; * var vX = new decimal?[n]; * var vY = new decimal?[n]; * var aX = new decimal?[n]; * var aY = new decimal?[n]; * * Time = new int[n]; * * var startTime = touches.First().Time; * * int i = 0; * foreach(var t in touches) * { * X[i] = t.X; * Y[i] = t.Y; * * Time[i] = (int)(t.Time - startTime); * * dT[i] = (i == 0) ? null : (int?)(Time[i] - Time[i - 1]); * * dX[i] = (i == 0) ? null : (decimal?)(X[i - 1] - X[i]); * dY[i] = (i == 0) ? null : (decimal?)(Y[i - 1] - Y[i]); * * ddX[i] = (i > 1) ? (decimal?)(dX[i-1] - dX[i]) : null; * ddY[i] = (i > 1) ? (decimal?)(dY[i-1] - dY[i]) : null; * * var dir = (dX[i].HasValue) ? (decimal?)Math.Atan2((double)dY[i], (double)dX[i]) : null; * var curv = (ddX[i].HasValue) ? (decimal?)((double)(dX[i] * ddY[i] - dY[i] * ddX[i]) / ((dX[i] == 0 && dY[i] == 0) ? 0 : Math.Pow((double)(dX[i] * dX[i] + dY[i] * dY[i]), 1.5d))) : null; * * var sdX = t.X - X[0]; * var sdY = t.Y - Y[0]; * var sdis = Math.Sqrt((double)(sdX * sdX + sdY * sdY)); * var sdir = Math.Atan2((double)sdY, (double)sdX); * * Direction[i] = dir; * Curvature[i] = curv; * StartDistance[i] = (decimal)sdis; * StartDirection[i] = (decimal)sdir; * * * vX[i] = (i == 0) ? null : (decimal?)(dX[i] / dT[i]); * vY[i] = (i == 0) ? null : (decimal?)(dY[i] / dT[i]); * * aX[i] = (i < 2) ? null : (decimal?)(ddX[i] / dT[i]); * aY[i] = (i < 2) ? null : (decimal?)(ddY[i] / dT[i]); * * i++; * } */ //create features and add to dictionary //var fX = new Feature("X", X); //var fY = new Feature("Y", Y); //var fXInt = new Feature("X interpolated", XInt); //var fYInt = new Feature("Y interpolated", YInt); //var fVx = new Feature("Velocity X", vX); //var fVy = new Feature("Velocity Y", vY); /* * var fStartDistance = new Feature("StartDistance", StartDistance); * var fStartDirection = new Feature("StartDirection", StartDirection); * var fDirection = new Feature("Direction", Direction.SkipWhile(d=>!d.HasValue).Cast<decimal>().ToArray()); * var fCurvature = new Feature("Curvature", Curvature.SkipWhile(d => !d.HasValue).Cast<decimal>().ToArray()); * var fVx = new Feature("Velocity X", vX.SkipWhile(d => !d.HasValue).Cast<decimal>().ToArray()); * var fVy = new Feature("Velocity Y", vY.SkipWhile(d => !d.HasValue).Cast<decimal>().ToArray()); * var fAx = new Feature("Acceleration X", aX.SkipWhile(d => !d.HasValue).Cast<decimal>().ToArray()); * var fAy = new Feature("Acceleration Y", aY.SkipWhile(d => !d.HasValue).Cast<decimal>().ToArray()); */ this["X"] = new Feature("X", splineX); this["Y"] = new Feature("Y", splineY); //this[fX.Name] = fX; //this[fY.Name] = fY; //this[fXInt.Name] = fXInt; //this[fYInt.Name] = fYInt; //this[fVx.Name] = fVx; //this[fVy.Name] = fVy; /* * this[fAx.Name] = fAx; * this[fAy.Name] = fAy; * this[fStartDistance.Name] = fStartDistance; * this[fStartDirection.Name] = fStartDirection; * this[fDirection.Name] = fDirection; * this[fCurvature.Name] = fCurvature; */ }
public override void CalculateTransmissionLoss(Platform platform, Mode mode, Radial radial, BottomProfile bottomProfile, SedimentType sedimentType, double windSpeed, IList<Tuple<double, SoundSpeedProfile>> soundSpeedProfilesAlongRadial) { var sourceDepth = platform.Depth; var frequency = (float)Math.Sqrt(mode.HighFrequency * mode.LowFrequency); if (mode.Depth.HasValue) sourceDepth += mode.Depth.Value; var directoryPath = Path.GetDirectoryName(radial.BasePath); if (directoryPath == null) throw new NullReferenceException("radial.BasePath does not point to a valid directory"); if (!Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath); // Derived Parameters // ================== // Note: All the specific calculations given in the comments below assume a frequency of 1kHz // lambda is wavelength, in meters var lambda = ReferenceSoundSpeed / frequency; // if dz < 1m round dz down to either [1/10, 1/5, 1/4 or 1/2] m ... or multiples of 10^-n of these numbers // = [1 2 2.5 or 5 ] x 0.1m " " ... // if dz > 1m round dz down to either [1 2 2.5 5 ] m ... or multiples of 10^+n of these numbers // var fixpoints = new List<double> { 1, 2, 2.5, 5 }; // dz = 0.1 * lambda var dz = RelativeDepthResolution * lambda; // make dz a 'pretty' number //dz = Fix2X10pN(dz, fixpoints); // ndz is the depth decimation factor // MinimumOutputDepthResolution is 10m // dz is 0.1 * lambda (dz = 0.15 for a 1 kHz signal, 'pretty' dz = 0.2 @ 1kHz) // so ndz = (10 / 0.2) = 50 @ 1kHz // this means that we will only output every 50 computational depth cells, giving us a depth // resolution of 50 * 0.2m = 10m @ 1kHz which is what we want it to be. Outstanding. var ndz = (int)Math.Max(1.0, Math.Floor(MinimumOutputDepthResolution / dz)); // similar for dr and assoc. grid decimation // RelativeRangeResolution is 2, so with our 'pretty' dz = 0.2, dr = 0.4 var dr = RelativeRangeResolution * dz; // make dr a 'pretty' number, in this case 0.25 //dr = Fix2X10pN(dr, fixpoints); // ndr is the range decimation factor // MinimumOutputRangeResolution is 10m // dr is 0.25 * lambda, so (10 / 0.25) gives us an ndr of 40 // this means that we will only output every 40 computational range cells, giving us a range // resolution of 40 * 0.25m = 10m @ 1kHz which is what we want it to be. Outstanding. var ndr = (int)Math.Max(1, Math.Floor(MinimumOutputRangeResolution / dr)); // attenuation layer (round up to nearest dz) var sedimentLambda = sedimentType.CompressionWaveSpeed / frequency; var sedimentLayerDz = Math.Ceiling(LastLayerThickness * sedimentLambda / dz) * dz; var attenuationLayerDz = Math.Ceiling(AttenuationLayerThickness * sedimentLambda / dz) * dz; var maxSubstrateDepth = bottomProfile.MaxDepth + sedimentLayerDz; var zstep = dz * ndz; var zmplt = Math.Ceiling((bottomProfile.MaxDepth + 2 * zstep) / zstep) * zstep; // Maximum Depth for PE calc -> zmax // zmax is the z-limit for the PE calc from top of the water column to the bottom of the last substrate layer // (including the attentuation layer if, as recommended, this is included) var zmax = maxSubstrateDepth + attenuationLayerDz; var envFileName = radial.BasePath + ".env"; //Debug.WriteLine("Scenario: '{0}' Mode: '{2}' Analysis point: {1} Bearing: {3}, zmplt: {4}", // radial.TransmissionLoss.AnalysisPoint.Scenario.Name, // radial.TransmissionLoss.AnalysisPoint.Geo, // radial.TransmissionLoss.Modes[0].ModeName, // radial.Bearing, zmplt); using (var envFile = new StreamWriter(envFileName, false)) { envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "Scenario: '{0}' Mode: '{2}' Analysis point: {1} Bearing: {3}", radial.TransmissionLoss.AnalysisPoint.Scenario.Name, radial.TransmissionLoss.AnalysisPoint.Geo, radial.TransmissionLoss.Modes[0].ModeName, radial.Bearing)); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.000000}\t{1:0.000000}\t{2:0.000000}\t\tf [Frequency (Hz)], zs [Source Depth (m)], zrec0 [First receiever depth (m)]", frequency, sourceDepth, 0.1)); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.000000}\t{1:0.000000}\t{2}\t\t\trmax[Max range (m)], dr [Range resolution (m)], ndr [Range grid decimation factor]", mode.MaxPropagationRadius + (dr * ndr), dr, ndr)); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.000000}\t{1:0.000000}\t{2}\t{3:0.000000}\tzmax [Max computational depth (m)], dz [Depth resolution (m)], ndz [Depth grid decimation factor], zmplot [Maximum depth to plot (m)]", zmax, dz, ndz, zmplt)); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.000000}\t{1}\t{2}\t{3:0.000000}\t\tc0 [Reference sound speed (m/s)], np [Number of terms in Padé expansion], ns [Number of stability constraints], rs [Maximum range of stability constraints (m)]", ReferenceSoundSpeed, PadeExpansionTerms, StabilityConstraints, StabilityConstraintMaxRange)); // todo: different stuff goes here for RAMSGeo // bathymetry data var first = true; foreach (var profilePoint in bottomProfile.Profile) { envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.000000}\t{1:0.000000}{2}", profilePoint.Range * 1000, profilePoint.Depth, first ? "\t\t\t\t\tbathymetry data [range (m), depth (m)]" : "")); first = false; } envFile.WriteLine("-1\t-1"); // range-dependent environment profiles var firstRangeProfile = true; foreach (var rangeProfileTuple in soundSpeedProfilesAlongRadial) { // Range of profile only written for second and subsequent profiles if (!firstRangeProfile) envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.#}\t\t\t\t\t\t\tProfile range (m)", rangeProfileTuple.Item1 * 1000)); var firstSoundSpeedProfile = true; foreach (var profilePoint in rangeProfileTuple.Item2.Data) { if (double.IsNaN(profilePoint.SoundSpeed)) break; envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.######}\t{1:0.######}{2}", profilePoint.Depth, profilePoint.SoundSpeed, firstSoundSpeedProfile ? "\t\t\t\t\tsound speed profile in water [depth (m), sound speed (m/s)]" : "")); firstSoundSpeedProfile = false; } envFile.WriteLine("-1\t-1"); // todo: RAMGeo and RAMSGeo also support sediment layers, as well as range-dependent sediment, neither of which is not yet supported by ESME // If sediment layers are ever supported, put a loop like for the sound speed profile above // A sediment layer is analogous to a sound speed profile point // For range-dependent sediment, the sediment samples have to be at the same ranges as the sound speed profiles // so we might want to change the API to include sediment properties in what is the current range and sound speed profile tuple envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.######}\t{1:0.######}\t\t\t\t\t\tcompressive sound speed profile in substrate [depth (m), sound speed (m/s)]", 0.0, sedimentType.CompressionWaveSpeed)); envFile.WriteLine("-1\t-1"); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.######}\t{1:0.######}\t\t\t\t\t\tdensity profile in substrate [depth (m), rhob (g/cm³)]", 0.0, sedimentType.Density)); envFile.WriteLine("-1\t-1"); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.######}\t{1:0.######}\t\t\t\t\t\tcompressive attenuation profile in substrate [depth (m), attnp (db/lambda)]", 0.0, 0.0)); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:0.######}\t{1:0.######}", attenuationLayerDz, 40)); envFile.WriteLine("-1\t-1"); firstRangeProfile = false; } } var tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(envFileName)); //Debug.WriteLine(string.Format("Env File: {0} temp path: {1}", envFileName, tempDirectory)); if (Directory.Exists(tempDirectory)) { var files = Directory.GetFiles(tempDirectory, "*.*"); foreach (var file in files) File.Delete(file); Directory.Delete(tempDirectory, true); } else if (File.Exists(tempDirectory)) File.Delete(tempDirectory); Directory.CreateDirectory(tempDirectory); File.Copy(envFileName, Path.Combine(tempDirectory, "ramgeo.in")); using (var steerableArrayFile = new StreamWriter(Path.Combine(tempDirectory, "sra.in"), false)) { // From http://www.activefrance.com/Antennas/Introduction%20to%20Phased%20Array%20Design.pdf // theta3 = 3dB beam width, in degrees // emitterSize = size of emitter array, in meters // theta3 = (0.886 * lambda / arrayLength) * 180 / pi // so, doing the algebra and solving for arrayLength, you get: // emitterSize = (0.886 * lambda) / (theta3 * (pi / 180)) var emitterSize = (0.886 * lambda) / (mode.VerticalBeamWidth * (Math.PI / 180.0)); var emitterCount = (int)(emitterSize / (dz * 2)); var emitterSpacing = 1.0; var weights = new List<double> { 1 }; if (emitterCount > 1) { emitterSpacing = emitterSize / (emitterCount - 1); // chebyshev window calculations for relative emitter strength across the array var discreteFourierTransform = new MathNet.Numerics.IntegralTransforms.Algorithms.DiscreteFourierTransform(); var r0 = Math.Pow(10, mode.SideLobeAttenuation / 20.0); var n = emitterCount - 1; var a = Complex.Cosh((1.0 / n) * Acosh(r0)); var am = new Complex[n]; for (var m = 0; m < n; m++) am[m] = a * Complex.Cos(Math.PI * m / n); var wm = new Complex[n]; var sign = 1; for (var i = 0; i < n; i++) { if (am[i].Magnitude > 1) wm[i] = sign * Complex.Cosh(n * Acosh(am[i])); else wm[i] = sign * Complex.Cos(n * Complex.Acos(am[i])); sign *= -1; } discreteFourierTransform.BluesteinInverse(wm, FourierOptions.Default); weights = wm.Select(e => e.Real).ToList(); weights[0] /= 2; weights.Add(weights[0]); var maxWeight = weights.Max(); for (var i = 0; i < weights.Count; i++) weights[i] /= maxWeight; } steerableArrayFile.WriteLine("{0}\t{1}\t{2}", emitterCount, emitterSpacing, mode.DepressionElevationAngle); for (var i = 0; i < emitterCount; i++) steerableArrayFile.WriteLine("{0}", weights[i]); } //File.Copy(Path.Combine(AssemblyLocation, "sra.in"), Path.Combine(tempDirectory, "sra.in")); //Debug.WriteLine(string.Format("Env File: {0} copied to: {1}", envFileName, tempDirectory)); // Now that we've got the files ready to go, we can launch bellhop to do the actual calculations var ramProcess = new Process { StartInfo = new ProcessStartInfo(Path.Combine(AssemblyLocation, "RAMGeo.exe")) { CreateNoWindow = true, UseShellExecute = false, RedirectStandardInput = false, RedirectStandardOutput = true, RedirectStandardError = true, WorkingDirectory = tempDirectory } }; if (radial.IsDeleted) throw new RadialDeletedByUserException(); ramProcess.Start(); try { ramProcess.PriorityClass = ProcessPriorityClass.Idle; } catch (InvalidOperationException) { } //ramProcess.BeginOutputReadLine(); while (!ramProcess.HasExited) { if (radial.IsDeleted) { ramProcess.Kill(); throw new RadialDeletedByUserException(); } Thread.Sleep(20); } var ramOutput = ramProcess.StandardOutput.ReadToEnd(); var ramError = ramProcess.StandardError.ReadToEnd(); if (ramProcess.ExitCode != 0) { Debug.WriteLine("RAMGeo process for radial {0} exited with error code {1:X}", radial.BasePath, ramProcess.ExitCode); Debug.WriteLine(ramError); Directory.Delete(tempDirectory, true); return; } //File.Delete(Path.Combine(tempDirectory, "ramgeo.in")); //File.Delete(radial.BasePath + ".grid"); //File.Move(Path.Combine(tempDirectory, "tl.grid"), radial.BasePath + ".grid"); //File.Delete(radial.BasePath + ".line"); //File.Move(Path.Combine(tempDirectory, "tl.line"), radial.BasePath + ".line"); //File.Delete(radial.BasePath + ".pgrid"); //File.Move(Path.Combine(tempDirectory, "p.grid"), radial.BasePath + ".pgrid"); //File.Delete(radial.BasePath + ".sra"); //File.Move(Path.Combine(tempDirectory, "sra.in"), radial.BasePath + ".sra"); using (var writer = new StreamWriter(radial.BasePath + ".bty")) writer.Write(bottomProfile.ToBellhopString()); if (File.Exists(Path.Combine(tempDirectory, "p.grid"))) { var pressures = ReadPGrid(Path.Combine(tempDirectory, "p.grid")); File.Copy(Path.Combine(tempDirectory, "p.grid"), radial.BasePath + ".pgrid", true); //File.Delete(radial.BasePath + ".pgrid"); if (pressures.Count == 0) { Debug.WriteLine("Temp directory: " + tempDirectory); Debug.WriteLine("RAMGeo stdout: " + ramOutput); Debug.WriteLine("RAMGeo stderr: " + ramError); Directory.Delete(tempDirectory, true); return; } var rangeCount = pressures.Count; var depthCount = pressures[0].Length; var rr = new double[rangeCount]; var rd = new double[depthCount]; for (var rangeIndex = 0; rangeIndex < rr.Length; rangeIndex++) rr[rangeIndex] = (rangeIndex + 1) * dr * ndr; for (var depthIndex = 0; depthIndex < rd.Length; depthIndex++) rd[depthIndex] = depthIndex * zstep; //Debug.WriteLine("Scenario: '{0}' Mode: '{2}' Analysis point: {1} Bearing: {3}, zmplt: {4}, zstep: {5}, maxDepth: {6}, fileName: {7}, reqDepthCells: {8}, actualDepthCells: {9}", // radial.TransmissionLoss.AnalysisPoint.Scenario.Name, // radial.TransmissionLoss.AnalysisPoint.Geo, // radial.TransmissionLoss.Modes[0].ModeName, // radial.Bearing, // zmplt, // zstep, // rd.Last(), // Path.GetFileNameWithoutExtension(radial.BasePath), // zmplt / zstep, // depthCount); var shadeFile = new ShadeFile(sourceDepth, frequency, rd, rr, pressures); shadeFile.Write(radial.BasePath + ".shd"); //BellhopOutput.WriteShadeFile(radial.BasePath + ".shd", sourceDepth, frequency, rd, rr, pressures); } else { //Debug.WriteLine("Scenario: {0} Analysis point: {1} Mode {2} Bearing {3}", // radial.TransmissionLoss.AnalysisPoint.Scenario.Name, // radial.TransmissionLoss.AnalysisPoint.Geo, // radial.TransmissionLoss.Modes[0].ModeName, // radial.Bearing); //Debug.WriteLine("p.grid file not found in RAMGeo output directory"); } Directory.Delete(tempDirectory, true); //Debug.WriteLine(string.Format("Env File: {0} temp directory deleted: {1}", envFileName, tempDirectory)); }