public static string GetRadialConfiguration(AcousticProperties acousticProperties, SoundSpeedProfile ssp, SedimentType sediment, 
                                                    double maxDepth, double maxRadius, List<double> ranges, List<double> depths, 
                                                    bool useSurfaceReflection, bool useVerticalBeamforming, bool generateArrivalsFile, int beamCount)
        {
            using (var sw = new StringWriter())
            {
                sw.WriteLine("'TL' ! Title");
                sw.WriteLine("{0} ! Frequency (Hz)", acousticProperties.Frequency);
                sw.WriteLine("1 ! NMedia"); // was NMEDIA in gui_genbellhopenv.m
                sw.WriteLine(useSurfaceReflection ? "'CFFT ' ! Top Option" : "'CVFT ' ! Top Option");

                sw.WriteLine("0  0.00 {0} ! N sigma depth", ssp.Data[ssp.Data.Count - 1].Depth);

                // If SSP is shallower than the bathymetry then extrapolate an SSP entry for the deepest part of the water
                //if (SSP.DepthVector[SSP.DepthVector.Length - 1] < RealBottomDepth_Meters)
                //    SoundSpeedProfile = ExtrapolateSSP(SoundSpeedProfile, RealBottomDepth_Meters);

                foreach (var sample in ssp.Data)
                    sw.WriteLine("{0:0.00} {1:0.00} 0.00 1.00 0.00 0.00 / ! z c cs rho", sample.Depth, sample.SoundSpeed);

                sw.WriteLine("'A*' 0.00 ! Bottom Option, sigma"); // A = Acoustic halfspace, ~ = read bathymetry file, 0.0 = bottom roughness (currently ignored)
                sw.WriteLine("{0} {1} {2} {3} {4} {5} / ! lower halfspace", maxDepth, sediment.CompressionWaveSpeed, sediment.ShearWaveSpeed, sediment.Density, sediment.LossParameter, 0);
                // Source and Receiver Depths and Ranges
                sw.WriteLine("1"); // Number of Source Depths
                sw.WriteLine("{0} /", acousticProperties.SourceDepth); // source depth
                sw.WriteLine("{0}", depths.Count); // Number of Receiver Depths
                foreach (var depth in depths) sw.Write("{0} ", depth);
                sw.WriteLine("/ ! Receiver Depths (m)");
                sw.WriteLine("{0}", ranges.Count); // Number of Receiver Ranges
                foreach (var range in ranges) sw.Write("{0} ", range);
                sw.WriteLine("/ ! Receiver Ranges (km)");

                if (generateArrivalsFile) sw.WriteLine("'aG'");  // aB
                else sw.WriteLine(useVerticalBeamforming ? "'IG*'" : "'I'");
                // if useVerticalBeamforming is true, then SBPFIL must be present (Source Beam Pattern file)
                sw.WriteLine("{0}", beamCount); // Number of beams
                //sw.WriteLine("0"); // Number of beams
                var verticalHalfAngle = acousticProperties.VerticalBeamWidth / 2;
                var angle1 = acousticProperties.DepressionElevationAngle - verticalHalfAngle;
                var angle2 = acousticProperties.DepressionElevationAngle + verticalHalfAngle;
                sw.WriteLine("{0} {1} /", angle1, angle2); // Beam fan half-angles (negative angles are toward the surface
                //sw.WriteLine("-60.00 60.00 /"); // Beam fan half-angles (negative angles are toward the surface
                //sw.WriteLine("{0:F} {1:F} {2:F} ! step zbox(meters) rbox(km)", experiment.TransmissionLossSettings.DepthCellSize, RealBottomDepth_Meters + 100, (bottomProfile.Length / 1000.0) * 1.01);
                sw.WriteLine("{0} {1} {2}", (ranges[0] / 2) * 1000, maxDepth, maxRadius);
                return sw.ToString();
            }
        }
        public static void CreateBellhopEnvironment(string outputDirectory, string name, 
                                                    double sourceDepth, double frequency, double verticalBeamWidth, double depressionElevationAngle, 
                                                    List<double> bathymetryRanges, List<double> bathymetryDepths, List<double> soundspeedDepths, 
                                                    List<double> soundspeedSpeeds, List<double> receiverRanges, List<double> receiverDepths, 
                                                    int sedimentType, int beamCount, double maxDepth)
        {
            if (!Directory.Exists(outputDirectory)) Directory.CreateDirectory(outputDirectory);

            // Write the bathymetry file
            var bathymetryFilename = Path.Combine(outputDirectory, name + ".bty");
            using (var writer = new StreamWriter(bathymetryFilename))
            {
                writer.WriteLine("'C'");
                writer.WriteLine(bathymetryRanges.Count);
                for (var index = 0; index < bathymetryRanges.Count; index++)
                    writer.WriteLine("{0} {1}", bathymetryRanges[index], bathymetryDepths[index]);
            }

            var acousticProperties = new AcousticProperties
            {
                HighFrequency = (float)frequency,
                LowFrequency = (float)frequency,
                DepressionElevationAngle = (float)depressionElevationAngle,
                SourceDepth = (float)sourceDepth,
                VerticalBeamWidth = (float)verticalBeamWidth,
            };
            var maxRadius = (int)Math.Ceiling(receiverRanges.Last() * 1.01); // Allow an extra 1% of range so the beams don't run off the end before they hit the last column of receivers

            var sspData = soundspeedDepths.Select((t, index) => new SoundSpeedSample((float)t, (float)soundspeedSpeeds[index])).ToList();
            var soundSpeedProfile = new SoundSpeedProfile
            {
                Data = sspData
            };
            var result = GetRadialConfiguration(acousticProperties, soundSpeedProfile, SedimentTypes.Find(sedimentType), 
                                                maxDepth, maxRadius, receiverRanges, receiverDepths, 
                                                false, true, true, beamCount);
            File.WriteAllText(Path.Combine(outputDirectory, name + ".env"), result, new ASCIIEncoding());
        }