void Copy(Scenario scenario) { Name = scenario.Name; Comments = scenario.Comments; ShowAllAnalysisPoints = scenario.ShowAllAnalysisPoints; ShowAllPerimeters = scenario.ShowAllPerimeters; ShowAllSpecies = scenario.ShowAllSpecies; StartTime = new TimeSpan(scenario.StartTime.Ticks); Duration = new TimeSpan(scenario.Duration.Ticks); TimePeriod = (TimePeriod)scenario.TimePeriod; Location = scenario.Location; Wind = scenario.Wind; SoundSpeed = scenario.SoundSpeed; Sediment = scenario.Sediment; Bathymetry = scenario.Bathymetry; // Here we map the old perimeter to the new perimeter so that the copied platform gets the proper perimeter var perimeterMap = new Dictionary<Guid, Guid>(); foreach (var perimeter in scenario.Perimeters) { var newPerimeter = new Perimeter(perimeter) { Scenario = this }; perimeterMap.Add(perimeter.Guid, newPerimeter.Guid); Perimeters.Add(newPerimeter); } var modeMap = new Dictionary<Guid, Guid>(); var allModes = new List<Mode>(); foreach (var platform in scenario.Platforms) { var newPlatform = new Platform(platform) { Scenario = this }; // Make sure the new perimeter gets the proper copied perimeter from the original scenario if (platform.Perimeter != null) newPlatform.Perimeter = Perimeters.Find(p => p.Guid == perimeterMap[platform.Perimeter.Guid]); Platforms.Add(newPlatform); foreach (var source in platform.Sources) { var newSource = new Source(source) { Platform = newPlatform }; newPlatform.Sources.Add(newSource); foreach (var mode in source.Modes) { var newMode = new Mode(mode) { Source = newSource }; modeMap.Add(mode.Guid, newMode.Guid); newSource.Modes.Add(newMode); allModes.Add(newMode); } } } foreach (var analysisPoint in scenario.AnalysisPoints) { var newAnalysisPoint = new AnalysisPoint(analysisPoint) { Scenario = this }; AnalysisPoints.Add(newAnalysisPoint); foreach (var transmissionLoss in analysisPoint.TransmissionLosses) { var newTransmissionLoss = new TransmissionLoss { AnalysisPoint = newAnalysisPoint, LayerSettings = new LayerSettings(transmissionLoss.LayerSettings) }; foreach (var mode in transmissionLoss.Modes) newTransmissionLoss.Modes.Add(allModes.Find(m => m.Guid == modeMap[mode.Guid])); newAnalysisPoint.TransmissionLosses.Add(newTransmissionLoss); foreach (var radial in transmissionLoss.Radials) { var newRadial = new Radial(radial) { TransmissionLoss = newTransmissionLoss }; newTransmissionLoss.Radials.Add(newRadial); newRadial.CopyFiles(radial); } } } foreach (var species in scenario.ScenarioSpecies) { var newSpecies = new ScenarioSpecies(species) { Scenario = this }; ScenarioSpecies.Add(newSpecies); newSpecies.CopyFiles(species); } }
public static TransmissionLoss ClosestTransmissionLoss(this Scenario scenario, Geo geo, Mode mode) { var closest = (from ap in scenario.AnalysisPoints from tl in ap.TransmissionLosses where tl.Modes[0].IsAcousticallyEquivalentTo(mode) let d = geo.DistanceKilometers(ap.Geo) orderby d select new { d, tl }).FirstOrDefault(); return closest != null ? closest.tl : null; }
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)); }
public abstract void CalculateTransmissionLoss(Platform platform, Mode mode, Radial radial, BottomProfile bottomProfile, SedimentType sedimentType, double windSpeed, IList<Tuple<double, SoundSpeedProfile>> soundSpeedProfilesAlongRadial);
void AddMode(Source source, string name, bool isNew, float frequency = 1000f, float depth = 0f, float maxPropagationRadius = 25000f) { var mode = new Mode { ActiveTime = 1f, Depth = depth, DepressionElevationAngle = 0f, HighFrequency = frequency, LowFrequency = frequency, MaxPropagationRadius = maxPropagationRadius, ModeName = name, ModeType = null, PulseInterval = new TimeSpan(0, 0, 0, 30), PulseLength = new TimeSpan(0, 0, 0, 0, 500), RelativeBeamAngle = 0, Source = source, SourceLevel = 200, VerticalBeamWidth = 180f, HorizontalBeamWidth = 90, IsNew = isNew, TransmissionLossPluginType = Globals.PluginManagerService[PluginType.TransmissionLossCalculator][PluginSubtype.Bellhop].DefaultPlugin.PluginIdentifier.Type, }; source.Modes.Add(mode); //source.Platform.Scenario.Add(mode); source.Platform.Scenario.UpdateAnalysisPoints(); }
internal void Log(Mode mode, string message) { LogBase(new LogEntry(mode), message); }
public ModePropertiesViewModel(Mode mode) { _editedMode = mode; _originalMode = new Mode(mode); ModeName = _editedMode.ModeName; ModeType = _editedMode.ModeType; Depth = _editedMode.Depth; SourceLevel = _editedMode.SourceLevel; LowFrequency = _editedMode.LowFrequency; HighFrequency = _editedMode.HighFrequency; PulseIntervalString = _editedMode.PulseInterval != null ? ((TimeSpan)_editedMode.PulseInterval).ToString(TimeSpanFormatString) : null; PulseLengthString = _editedMode.PulseLength != null ? ((TimeSpan)_editedMode.PulseLength).ToString(TimeSpanFormatString) : null; HorizontalBeamWidth = _editedMode.HorizontalBeamWidth; VerticalBeamWidth = _editedMode.VerticalBeamWidth; DepressionElevationAngle = _editedMode.DepressionElevationAngle; RelativeBeamAngle = _editedMode.RelativeBeamAngle; MaxPropagationRadius = _editedMode.MaxPropagationRadius; ValidRadialCounts = new List<string> { "Auto", "4", "8", "16", "32", "64", "128" }; RadialCountString = _editedMode.RadialCount == 0 ? ValidRadialCounts[0] : _editedMode.RadialCount.ToString(CultureInfo.InvariantCulture); SideLobeAttenuation = _editedMode.SideLobeAttenuation; AvailableTransmissionLossEngines.AddRange(from key in Globals.PluginManagerService[PluginType.TransmissionLossCalculator].Keys select (TransmissionLossCalculatorPluginBase)Globals.PluginManagerService[PluginType.TransmissionLossCalculator][key].DefaultPlugin); SelectedTransmissionLossEngine = _editedMode.GetTransmissionLossPlugin(Globals.PluginManagerService); _propertyObserver = new PropertyObserver<ModePropertiesViewModel>(this) .RegisterHandler(p => p.SelectedTransmissionLossEngine, () => { }); WindowTitle = string.Format("Mode properties: {0}", _editedMode.ModeName); AddValidationRules( new ValidationRule<ModePropertiesViewModel> { PropertyName = "Depth", Description = "Cannot be negative", IsRuleValid = (target, rule) => target.Depth.HasValue && target.Depth.Value >= 0.0f, } , new ValidationRule<ModePropertiesViewModel> { PropertyName = "SourceLevel", Description = "Must be greater than zero", IsRuleValid = (target, rule) => target.SourceLevel > 0, }, new ValidationRule<ModePropertiesViewModel> { PropertyName = "HighFrequency", Description = "Must be greater than zero", IsRuleValid = (target, rule) => target.HighFrequency > 0, }, new ValidationRule<ModePropertiesViewModel> { PropertyName = "PulseIntervalString", Description = "Must be a valid, non-negative time span value in the format hh:mm:ss.fff where 00 <= hh <= 23; 00 <= mm <= 59; 00 <= ss <= 59; 000 <= fff <= 999", IsRuleValid = (target, rule) => { if (string.IsNullOrEmpty(target.PulseIntervalString)) return false; TimeSpan timeSpan; var isOK = TimeSpan.TryParseExact(target.PulseIntervalString, TimeSpanFormatString, null, out timeSpan); return isOK && timeSpan.Ticks > 0; }, }, new ValidationRule<ModePropertiesViewModel> { PropertyName = "PulseLengthString", Description = "Must be a valid, non-negative time span value in the format hh:mm:ss.fff where 00 <= hh <= 23; 00 <= mm <= 59; 00 <= ss <= 59; 000 <= fff <= 999", IsRuleValid = (target, rule) => { if (string.IsNullOrEmpty(target.PulseLengthString)) return false; TimeSpan timeSpan; var isOK = TimeSpan.TryParseExact(target.PulseLengthString, TimeSpanFormatString, null, out timeSpan); return isOK && timeSpan.Ticks > 0; }, }, new ValidationRule<ModePropertiesViewModel> { PropertyName = "RelativeBeamAngle", Description = "Must be between -180 and 180, inclusive", IsRuleValid = (target, rule) => -180 <= target.RelativeBeamAngle && target.RelativeBeamAngle <= 180, }, new ValidationRule<ModePropertiesViewModel> { PropertyName = "HorizontalBeamWidth", Description = "Must be a positive value less than or equal to 360", IsRuleValid = (target, rule) => 0 < target.HorizontalBeamWidth && target.HorizontalBeamWidth <= 360, }, new ValidationRule<ModePropertiesViewModel> { PropertyName = "VerticalBeamWidth", Description = "Must be a positive value less than or equal to 180", IsRuleValid = (target, rule) => 0 < target.VerticalBeamWidth && target.VerticalBeamWidth <= 180, }, new ValidationRule<ModePropertiesViewModel> { PropertyName = "DepressionElevationAngle", Description = "Must be between -90 and 90, inclusive", IsRuleValid = (target, rule) => -90 <= target.DepressionElevationAngle && target.DepressionElevationAngle <= 90, }, new ValidationRule<ModePropertiesViewModel> { PropertyName = "MaxPropagationRadius", Description = "Must be a positive value", IsRuleValid = (target, rule) => target.MaxPropagationRadius > 0, }); }
/// <summary> /// True if this mode is acoustically equivalent to the other mode /// Acoustic equivalence means that the following are ALL TRUE /// depths are within 0.001 m /// vertical beam widths are within 0.1 deg /// depression/elevation angles are within 0.1 deg /// high frequencies are within 0.1 Hz /// low frequencies are within 0.1 Hz /// /// As of 22 Feb 2013, the transmission loss plugins selected to /// compute the sound fields must also be identical /// </summary> /// <param name="other"></param> /// <returns></returns> public bool IsAcousticallyEquivalentTo(Mode other) { var myDepth = Source.Platform.Depth; if (Depth.HasValue) myDepth += Depth.Value; var otherDepth = other.Source.Platform.Depth; if (other.Depth.HasValue) otherDepth += other.Depth.Value; if (Math.Abs(myDepth - otherDepth) > 0.001) return false; if (Math.Abs(VerticalBeamWidth - other.VerticalBeamWidth) > 0.1) return false; if (Math.Abs(DepressionElevationAngle - other.DepressionElevationAngle) > 0.1) return false; if (Math.Abs(HighFrequency - other.HighFrequency) > 0.1) return false; if (Math.Abs(LowFrequency - other.LowFrequency) > 0.1) return false; return TransmissionLossPluginType == other.TransmissionLossPluginType; }
void ModeProperties(Mode mode) { var vm = new ModePropertiesViewModel(mode); var result = Globals.VisualizerService.ShowDialog("ModePropertiesWindowView", vm); if (!(result.HasValue && result.Value)) return; mode.LowFrequency = mode.HighFrequency; Scenario.UpdateAnalysisPoints(); }
public void PlatformBehaviorToKML() { var jaxOpsArea = new GeoArray(new Geo(29.3590, -79.2195), new Geo(31.1627, -79.2195), new Geo(31.1627, -81.2789), new Geo(30.1627, -81.2789), new Geo(29.3590, -80.8789), new Geo(29.3590, -79.2195)); var platform = new Platform { PlatformName = "Test Platform", Perimeter = jaxOpsArea, Depth = 0, IsRandom = true, TrackType = TrackType.PerimeterBounce, Sources = new ObservableList<Source>(), Speed = 20, }; var source = new Source { SourceName = "Test Source", Modes = new ObservableList<Mode>(), Platform = platform, }; platform.Sources.Add(source); var mode = new Mode { ModeName = "Test Mode", PulseInterval = new TimeSpan(0, 0, 0, 10), PulseLength = new TimeSpan(0, 0, 0, 0, 500), Depth = 5, HighFrequency = 1000, LowFrequency = 1000, DepressionElevationAngle = 10, VerticalBeamWidth = 90, SourceLevel = 200, Source = source, }; source.Modes.Add(mode); var behavior = new PlatformBehavior(platform, new TimeSpan(0, 0, 0, 1), 86400); #if true var kml = new KMLRoot(); var folder = new Folder("Jacksonville"); jaxOpsArea.Placemark.name = "Jacksonville OpArea"; jaxOpsArea.Placemark.Snippet = "The operational area"; jaxOpsArea.Placemark.Snippet.maxLines = 1; folder.Add(jaxOpsArea.Placemark); #if true var timeStep = 0; foreach (var state in behavior.PlatformStates) { if (timeStep % 100 == 0) { state.PlatformLocation.Location.Placemark.name = string.Format("TimeStep {0}", timeStep); folder.Add(state.PlatformLocation.Location.Placemark); } timeStep++; } #else result.Placemark.name = "Platform track"; result.Placemark.Snippet = "The track of the platform"; result.Placemark.Snippet.maxLines = 1; result.Placemark.Geometry.AltitudeMode = AltitudeMode.clampedToGround; folder.Add(result.Placemark); #endif kml.Document.Add(folder); var savePath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments), "Platform Behavior Tests", "PlatformBehavior.kml"); Debug.WriteLine("Saving KML..."); kml.Save(savePath); #endif }
void RecalculateMode(Mode mode) { if (Globals.MessageBoxService.ShowYesNo(string.Format("Are you sure you want to recalculate all transmission losses for the mode \"{0}\"?", mode.ModeName), MessageBoxImage.Warning) != MessageBoxResult.Yes) return; foreach (var transmissionLoss in mode.TransmissionLosses) transmissionLoss.Recalculate(); }
void DeleteMode(Mode mode) { if (Globals.MessageBoxService.ShowYesNo(string.Format("Are you sure you want to delete the mode \"{0}\"?", mode.ModeName), MessageBoxImage.Warning) != MessageBoxResult.Yes) return; mode.Delete(); OnPropertyChanged("CanPlaceAnalysisPoint"); OnPropertyChanged("IsSaveScenarioCommandEnabled"); }
async void ModeBoundToLayer(Mode mode) { if (!mode.IsNew) return; mode.IsNew = false; ((LayerControl)mode.LayerControl).Select(); await Task.Delay(50); ((LayerControl)mode.LayerControl).Edit(); }
public Mode(Mode mode) { Copy(mode); }
public override void CalculateTransmissionLoss(Platform platform, Mode mode, Radial radial, BottomProfile bottomProfile, SedimentType sedimentType, double windSpeed, IList<Tuple<double, SoundSpeedProfile>> soundSpeedProfilesAlongRadial) { CalculateTransmissionLossInternal(platform, mode, radial, bottomProfile, sedimentType, windSpeed, soundSpeedProfilesAlongRadial, false); }
void Copy(Mode mode) { PSMModeGuid = mode.PSMModeGuid; ModeName = mode.ModeName; ModeType = mode.ModeType; ActiveTime = mode.ActiveTime; Depth = mode.Depth; SourceLevel = mode.SourceLevel; LowFrequency = mode.LowFrequency; HighFrequency = mode.HighFrequency; PulseInterval = mode.PulseInterval; PulseLength = mode.PulseLength; HorizontalBeamWidth = mode.HorizontalBeamWidth; VerticalBeamWidth = mode.VerticalBeamWidth; DepressionElevationAngle = mode.DepressionElevationAngle; RelativeBeamAngle = mode.RelativeBeamAngle; MaxPropagationRadius = mode.MaxPropagationRadius; TransmissionLossPluginType = mode.TransmissionLossPluginType; RadialCount = mode.RadialCount; Source = mode.Source; }
protected void CalculateTransmissionLossInternal(Platform platform, Mode mode, Radial radial, BottomProfile bottomProfile, SedimentType sedimentType, double windSpeed, IList<Tuple<double, SoundSpeedProfile>> soundSpeedProfilesAlongRadial, bool createArrivalsFile) { var depthCellCount = (int)Math.Ceiling(bottomProfile.MaxDepth / DepthCellSize); var rangeCellCount = (int)Math.Ceiling(mode.MaxPropagationRadius / RangeCellSize); var startProfile = soundSpeedProfilesAlongRadial[0].Item2; var sourceDepth = platform.Depth; var frequency = (float)Math.Sqrt(mode.HighFrequency * mode.LowFrequency); if (mode.Depth.HasValue) sourceDepth += mode.Depth.Value; var maxCalculationDepthMeters = bottomProfile.MaxDepth * 1.01; 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); using (var envFile = new StreamWriter(radial.BasePath + ".env", false)) { envFile.WriteLine("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}", frequency)); envFile.WriteLine("1"); // was NMEDIA in gui_genbellhopenv.m envFile.WriteLine(UseSurfaceReflection ? "'QFLT'" : "'QVLT'"); //if (depthCellCount < 5) throw new BathymetryTooShallowException("Error: Maximum depth of transect (" + maxCalculationDepthMeters + " meters) less than minimum required for transmission loss calculations.\nPlease choose a different location for this transect."); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "0, 0.0, {0}", startProfile.Data[startProfile.Data.Count - 1].Depth)); foreach (var soundSpeedSample in startProfile.Data) envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} 0.0 1.0 0.0 0.0", soundSpeedSample.Depth, soundSpeedSample.SoundSpeed)); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "'A*' 0.0")); // A = Acoustic halfspace, * = read bathymetry file 'BTYFIL', 0.0 = bottom roughness (currently ignored) envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3} {4} {5} /", maxCalculationDepthMeters, sedimentType.CompressionWaveSpeed, sedimentType.ShearWaveSpeed, sedimentType.Density, sedimentType.LossParameter, 0)); // Source and Receiver Depths and Ranges envFile.WriteLine("1"); // Number of Source Depths envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} /", sourceDepth)); // source depth envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", depthCellCount)); // Number of Receiver Depths envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "0.0 {0} /", maxCalculationDepthMeters)); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", rangeCellCount)); // Number of receiver ranges envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "0.0 {0} /", mode.MaxPropagationRadius / 1000.0)); envFile.WriteLine(createArrivalsFile ? "'a'" : "'I'"); envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}", RayCount)); // Number of beams var verticalHalfAngle = mode.VerticalBeamWidth / 2; var angle1 = mode.DepressionElevationAngle - verticalHalfAngle; var angle2 = mode.DepressionElevationAngle + verticalHalfAngle; envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} /", angle1, angle2)); // Beam fan half-angles (negative angles are toward the surface envFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "0.0 {0} {1}", maxCalculationDepthMeters, (mode.MaxPropagationRadius / 1000.0) * 1.01)); // step zbox(meters) rbox(km) } using (var sspFile = new StreamWriter(radial.BasePath + ".ssp", false)) { if (soundSpeedProfilesAlongRadial.Count == 1) soundSpeedProfilesAlongRadial.Add(Tuple.Create(Geo.RadiansToKilometers(radial.Segment.LengthRadians), new SoundSpeedProfile(soundSpeedProfilesAlongRadial[0].Item2))); sspFile.WriteLine("{0}", soundSpeedProfilesAlongRadial.Count); foreach (var rangeProfileTuple in soundSpeedProfilesAlongRadial) sspFile.Write(string.Format(CultureInfo.InvariantCulture, "{0,-10:0.###}", rangeProfileTuple.Item1)); sspFile.WriteLine(); //sspFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0,-10:0.###}{1,-10:0.###}{2,-10:0.###}", 0.0, bottomProfile.Profile[bottomProfile.Profile.Count / 2].Range, bottomProfile.Profile[bottomProfile.Profile.Count - 1].Range)); for (var depthIndex = 0; depthIndex < startProfile.Data.Count; depthIndex++) { foreach (var rangeProfileTuple in soundSpeedProfilesAlongRadial) sspFile.Write(string.Format(CultureInfo.InvariantCulture, "{0,-10:0.###}", rangeProfileTuple.Item2.Data[depthIndex].SoundSpeed)); sspFile.WriteLine(); } //sspFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0,-10:0.###}{1,-10:0.###}{2,-10:0.###}", startProfile.Data[depthIndex].SoundSpeed, middleProfile.Data[depthIndex].SoundSpeed, endProfile.Data[depthIndex].SoundSpeed)); } using (var trcFile = new StreamWriter(radial.BasePath + ".trc", false)) { var topReflectionCoefficients = GenerateReflectionCoefficients(windSpeed, frequency); trcFile.WriteLine(topReflectionCoefficients.GetLength(0)); for (var rowIndex = 0; rowIndex < topReflectionCoefficients.GetLength(0); rowIndex++) trcFile.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} ", topReflectionCoefficients[rowIndex, 0], topReflectionCoefficients[rowIndex, 1], topReflectionCoefficients[rowIndex, 2])); } using (var writer = new StreamWriter(radial.BasePath + ".bty")) writer.Write(bottomProfile.ToBellhopString()); // Now that we've got the files ready to go, we can launch bellhop to do the actual calculations var bellhopProcess = new Process { StartInfo = new ProcessStartInfo(Path.Combine(AssemblyLocation, "bellhop.exe"), radial.Filename) { CreateNoWindow = true, UseShellExecute = false, RedirectStandardInput = false, RedirectStandardOutput = true, RedirectStandardError = true, WorkingDirectory = directoryPath } }; if (radial.IsDeleted) throw new RadialDeletedByUserException(); bellhopProcess.Start(); try { bellhopProcess.PriorityClass = ProcessPriorityClass.Idle; } catch (InvalidOperationException) {} //bellhopProcess.BeginOutputReadLine(); while (!bellhopProcess.HasExited) { if (radial.IsDeleted) { bellhopProcess.Kill(); throw new RadialDeletedByUserException(); } Thread.Sleep(20); } if (bellhopProcess.ExitCode == 0) return; var bellhopOutput = bellhopProcess.StandardOutput.ReadToEnd(); var bellhopError = bellhopProcess.StandardError.ReadToEnd(); Debug.WriteLine("Bellhop process for radial {0} exited with error code {1:X}", radial.BasePath, bellhopProcess.ExitCode); Debug.WriteLine("Bellhop stdout: " + bellhopOutput); Debug.WriteLine("Bellhop stderr: " + bellhopError); radial.CleanupFiles(); }
public int GetHashCode(Mode obj) { return obj.GetHashCode(); }
static void UpdateFootprintMapLayer(Mode mode, PlatformState state, OverlayShapeMapLayer mapLayer) { mapLayer.Clear(); var initialGeo = state.PlatformLocation.Location; var footprintArcStartBearing = state.PlatformLocation.Course + mode.RelativeBeamAngle - (Math.Abs(mode.HorizontalBeamWidth / 2)); var footprintArcEndBearing = state.PlatformLocation.Course + mode.RelativeBeamAngle + (Math.Abs(mode.HorizontalBeamWidth / 2)); var geos = new List<Geo>(); if (mode.HorizontalBeamWidth < 360) geos.Add(initialGeo); for (var arcPointBearing = footprintArcStartBearing; arcPointBearing < footprintArcEndBearing; arcPointBearing++) geos.Add(initialGeo.Offset(Geo.MetersToRadians(mode.MaxPropagationRadius), Geo.DegreesToRadians(arcPointBearing))); geos.Add(initialGeo.Offset(Geo.MetersToRadians(mode.MaxPropagationRadius), Geo.DegreesToRadians(footprintArcEndBearing))); geos.Add(mode.HorizontalBeamWidth < 360 ? initialGeo : initialGeo.Offset(Geo.MetersToRadians(mode.MaxPropagationRadius), Geo.DegreesToRadians(footprintArcStartBearing))); mapLayer.AddPolygon(geos); mapLayer.Done(); }