예제 #1
0
        public static double CalculateEND(DepthFactor depthF, BreathGas gas)
        {
            var air   = new BreathGas();
            var ppN   = DivingMath.SeaLevelPreasureBars * BreathGas.GetGasPartialPreasureForDepth(depthF, gas.PpN);
            var depth = DivingMath.PreasureBarsToDepth(ppN / air.PpN, depthF.WaterDensity);

            return(Math.Round(depth.Depth, 1));
        }
예제 #2
0
        private KeyValuePair <IEnumerable <ConsumedGas>, IEnumerable <ConsumedGas> > CalculateConsumedGas(
            CalculatedDiveResult diveResult, IEnumerable <GasSwitch> gasSwitches)
        {
            DivePoint?prevPoint   = null;
            var       bottomGases = new Dictionary <BreathGas, ConsumedGas>(sizeof(double));
            var       decoGases   = new Dictionary <BreathGas, ConsumedGas>(sizeof(double));

            var firstStopDepth = diveResult.DecoStops?.Any() == true?diveResult.DecoStops.Max(s => s.Depth) : 0;

            if (firstStopDepth < double.Epsilon)
            {
                firstStopDepth = _diveParameters.DiveConfig.SafeStopDepth;
            }

            foreach (var point in diveResult.DivePoints)
            {
                if (prevPoint != null)
                {
                    var avgDepth = (prevPoint.Value.DepthFactor.Depth + point.DepthFactor.Depth) * 0.5;
                    if (avgDepth <= double.Epsilon)
                    {
                        break;
                    }

                    var timeSpan = point.CurrentDiveTime - prevPoint.Value.CurrentDiveTime;
                    if (timeSpan > 0)
                    {
                        var preasure = DivingMath.DepthToPreasureBars(new DepthFactor(avgDepth, point.DepthFactor.WaterDensity));
                        var rmv      = (avgDepth <= firstStopDepth) ? _diveParameters.DiveConfig.DecoRmv : _diveParameters.DiveConfig.BottomRmv;

                        var decoSwitchTime = gasSwitches.FirstOrDefault(gs => gs.IsDeco)?.AbsoluteTime ?? 0.0;
                        var gases          = (point.CurrentDiveTime >= decoSwitchTime && decoSwitchTime > double.Epsilon) ? decoGases : bottomGases;

                        if (!gases.ContainsKey(point.CurrentGas))
                        {
                            gases[point.CurrentGas] = new ConsumedGas()
                            {
                                Gas = point.CurrentGas
                            }
                        }
                        ;

                        gases[point.CurrentGas].Amount += rmv * preasure / DivingMath.SeaLevelPreasureBars * timeSpan;
                    }
                }

                prevPoint = point;
            }

            return(new KeyValuePair <IEnumerable <ConsumedGas>, IEnumerable <ConsumedGas> >(bottomGases.Values, decoGases.Values));
        }
    }
예제 #3
0
        private double GetCurrentCeilingDepth(double currentDepth, double?customGF = null, CompartmentCalculations[] tissues = null)
        {
            int    index      = 0;
            double maxDepth   = 0;
            double gradFactor = customGF ?? 0;

            if (gradFactor <= double.Epsilon)
            {
                if (_firstDecoStopDepth < double.Epsilon)
                {
                    gradFactor = _diveParameters.DiveConfig.GradFactorLow;
                }
                else
                {
                    currentDepth -= DecoStopsStep;
                    if (currentDepth < 0)
                    {
                        currentDepth = 0;
                    }

                    gradFactor = _diveParameters.DiveConfig.GradFactorHigh -
                                 (_diveParameters.DiveConfig.GradFactorHigh - _diveParameters.DiveConfig.GradFactorLow) / _firstDecoStopDepth * currentDepth;
                }
            }

            foreach (var cpt in _compartments)
            {
                var data   = (tissues ?? _compartmentData)[index];
                var pTotal = data.HePresure + data.N2Presure;

                var bulmanA = ((cpt.N2ValueA * data.N2Presure) + (cpt.HeValueA * data.HePresure)) / pTotal;
                var bulmanB = ((cpt.N2ValueB * data.N2Presure) + (cpt.HeValueB * data.HePresure)) / pTotal;

                var tolerantPreasure = (pTotal - (bulmanA * gradFactor)) / ((gradFactor / bulmanB) + 1.0 - gradFactor);
                var tolerantDepth    = DivingMath.PreasureBarsToDepth(tolerantPreasure, _waterDensity);

                if (tolerantDepth.Depth > maxDepth)
                {
                    maxDepth = tolerantDepth.Depth;
                }

                ++index;
            }

            return(maxDepth);
        }
예제 #4
0
        public CalculatedDivePlan Calculate(CalculatedDivePlan prevDive, DiveParameters diveParameters)
        {
            var    points       = new List <DivePlanPoint>(sizeof(double));
            var    bottomLevels = new List <LevelInfo>(diveParameters.Levels.Count());
            double totalTime    = 0;

            _diveParameters = diveParameters;

            var plan = new CalculatedDivePlan
            {
                MaxDepth       = _diveParameters.Levels.Max(l => l.DepthFactor.Depth),
                BottomTime     = _diveParameters.Levels.Sum(l => l.Time),
                PlanPoints     = points,
                MaxPpO         = _diveParameters.Levels.Max(l => BreathGas.GetGasPartialPreasureForDepth(l.DepthFactor, l.Gas.PpO)),
                MaxPpN         = _diveParameters.Levels.Max(l => BreathGas.GetGasPartialPreasureForDepth(l.DepthFactor, l.Gas.PpN)),
                DiveParameters = _diveParameters
            };

            var           diveResult = _diveAlgorythm.ProcessDive(_diveParameters, prevDive?.TissuesSaturationData);
            DivePlanPoint lastPoint  = null;

            if (diveResult.Errors?.Any() == true)
            {
                return new CalculatedDivePlan {
                           Errors = diveResult.Errors
                }
            }
            ;

            // dive start
            points.Add(new DivePlanPoint
            {
                Depth        = 0,
                AbsoluteTime = 0,
                Duration     = 0,
                Type         = DivePlanPointType.StartDive,
                Gas          = _diveParameters.Levels.First().Gas
            });

            lastPoint = points[0];

            foreach (var level in _diveParameters.Levels)
            {
                var depthDistance = Math.Abs(lastPoint.Depth - level.Depth);

                var descend = level.Depth > lastPoint.Depth;

                if (lastPoint.Type != DivePlanPointType.StartDive)
                {
                    lastPoint.Type = DivePlanPointType.Bottom | (descend ? DivePlanPointType.Descent : DivePlanPointType.Ascent);
                }

                var goToLevelTime = descend ?
                                    depthDistance / _diveParameters.DiveConfig.MaxDescentSpeed :
                                    depthDistance / _diveParameters.DiveConfig.MaxAscentSpeed;

                totalTime += goToLevelTime;
                var reachedTime = totalTime;

                points.Add(new DivePlanPoint
                {
                    Depth        = level.Depth,
                    AbsoluteTime = totalTime,
                    Duration     = goToLevelTime,
                    Gas          = level.Gas,
                    Type         = (descend ? DivePlanPointType.Descent : DivePlanPointType.Ascent) | DivePlanPointType.Bottom
                });

                totalTime += level.Time;

                points.Add(new DivePlanPoint
                {
                    Depth        = level.Depth,
                    AbsoluteTime = totalTime,
                    Duration     = level.Time,
                    Gas          = level.Gas,
                    Type         = DivePlanPointType.Bottom
                });

                bottomLevels.Add(new LevelInfo
                {
                    Depth       = level.Depth,
                    PpO         = BreathGas.GetGasPartialPreasureForDepth(level.DepthFactor, level.Gas.PpO),
                    END         = DivingMath.CalculateEND(level.DepthFactor, level.Gas),
                    Gas         = level.Gas,
                    TimeReached = reachedTime
                });

                lastPoint = points.Last();
            }

            lastPoint.Type = DivePlanPointType.Ascent | DivePlanPointType.Bottom | DivePlanPointType.FinalAscent;

            // ascend & deco
            if (diveResult.DecoStops?.Any() == true)
            {
                double lastDepth = lastPoint.Depth;

                foreach (var deco in diveResult.DecoStops)
                {
                    var decoAscendTime = (lastDepth - deco.Depth) / _diveParameters.DiveConfig.MaxAscentSpeed;
                    totalTime += decoAscendTime;

                    points.Add(new DivePlanPoint
                    {
                        Depth        = deco.Depth,
                        AbsoluteTime = totalTime,
                        Duration     = deco.Time,
                        Gas          = deco.Gas,
                        Type         = DivePlanPointType.Deco
                    });

                    totalTime += deco.Time;

                    points.Add(new DivePlanPoint
                    {
                        Depth        = deco.Depth,
                        AbsoluteTime = totalTime,
                        Duration     = deco.Time,
                        Gas          = deco.Gas,
                        Type         = DivePlanPointType.Deco | DivePlanPointType.FinalAscent
                    });

                    lastDepth = deco.Depth;
                }

                var ascendTime = lastDepth / _diveParameters.DiveConfig.MaxAscentSpeed;
                totalTime += ascendTime;
            }
            else
            {
                // ascend
                var ascendTime = (lastPoint.Depth - _diveParameters.DiveConfig.SafeStopDepth) / _diveParameters.DiveConfig.MaxAscentSpeed;
                totalTime += ascendTime;

                var avrAscDepth = (lastPoint.Depth + _diveParameters.DiveConfig.SafeStopDepth) / 2;

                if (lastPoint.Depth <= _diveParameters.DiveConfig.SafeStopDepth)
                {
                    // no safety stop
                    totalTime += lastPoint.Depth / _diveParameters.DiveConfig.MaxAscentSpeed;
                }
                else
                {
                    // safety stop
                    points.Add(new DivePlanPoint
                    {
                        Depth        = _diveParameters.DiveConfig.SafeStopDepth,
                        AbsoluteTime = totalTime,
                        Duration     = _diveParameters.DiveConfig.SafeStopTime,
                        Gas          = _diveParameters.Levels.First().Gas,
                        Type         = DivePlanPointType.SafeStop
                    });

                    totalTime += _diveParameters.DiveConfig.SafeStopTime;

                    points.Add(new DivePlanPoint
                    {
                        Depth        = _diveParameters.DiveConfig.SafeStopDepth,
                        AbsoluteTime = totalTime,
                        Duration     = _diveParameters.DiveConfig.SafeStopTime,
                        Gas          = _diveParameters.Levels.First().Gas,
                        Type         = DivePlanPointType.SafeStop | DivePlanPointType.FinalAscent
                    });

                    ascendTime = _diveParameters.DiveConfig.SafeStopDepth / _diveParameters.DiveConfig.MaxAscentSpeed;
                    totalTime += ascendTime;
                }
            }

            // end dive
            points.Add(new DivePlanPoint {
                Depth = 0, AbsoluteTime = totalTime, Type = DivePlanPointType.EndDive, Gas = new BreathGas()
            });

            var gasSwitches = new List <GasSwitch>();

            plan.FullDesaturationTime   = diveResult.FullDesaturationTime;
            plan.TissuesSaturationData  = diveResult.TissuesSaturationData;
            plan.TotalTime              = diveResult.DiveTotalTime;
            plan.MaxNoDecoDepthTime     = diveResult.MaxNoDecoDepthTime;
            plan.DynamicNoDecoDepthTime = diveResult.DynamicNoDecoDepthTime;
            plan.IntervalTime           = _diveParameters.IntervalTime;
            plan.OxygenCns              = OxygenToxicityCalculator.CalulateOxygenCns(diveResult.DivePoints);
            plan.MaxEND      = _diveParameters.Levels.Max(l => DivingMath.CalculateEND(l.DepthFactor, l.Gas));
            plan.GasSwitches = gasSwitches;
            plan.LevelsInfo  = bottomLevels;

            foreach (var level in _diveParameters.Levels.Skip(1))
            {
                // use strict reference comparsion for presise gas check
                var gasSwitchPoint = diveResult.DivePoints.FirstOrDefault(d => d.CurrentGas == level.Gas);
                if (!gasSwitchPoint.IsEmpty())
                {
                    gasSwitches.Add(new GasSwitch
                    {
                        Depth        = gasSwitchPoint.DepthFactor.Depth,
                        AbsoluteTime = gasSwitchPoint.CurrentDiveTime,
                        Gas          = gasSwitchPoint.CurrentGas,
                        IsDeco       = false,
                        PpO          = BreathGas.GetGasPartialPreasureForDepth(gasSwitchPoint.DepthFactor, gasSwitchPoint.CurrentGas.PpO)
                    });
                }
            }

            foreach (var decoLevel in _diveParameters.DecoLevels ?? new List <DiveLevel>())
            {
                // use strict reference comparsion for presise gas check
                var decoGasSwitchPoint = diveResult.DivePoints.FirstOrDefault(d => d.CurrentGas == decoLevel.Gas);
                if (!decoGasSwitchPoint.IsEmpty())
                {
                    gasSwitches.Add(new GasSwitch
                    {
                        Depth        = decoGasSwitchPoint.DepthFactor.Depth,
                        AbsoluteTime = decoGasSwitchPoint.CurrentDiveTime,
                        Gas          = decoGasSwitchPoint.CurrentGas,
                        IsDeco       = true,
                        PpO          = BreathGas.GetGasPartialPreasureForDepth(decoGasSwitchPoint.DepthFactor, decoGasSwitchPoint.CurrentGas.PpO)
                    });
                }
            }

            // celing points reduce logic
            if (diveResult.CeilingDepthPoints?.Any() == true)
            {
                const int maxPoints = 100;
                var       skip      = 2 * diveResult.CeilingDepthPoints.Count() / 2 / maxPoints;
                if (skip == 0)
                {
                    skip = 1;
                }

                var ceilingDepthPoints = new List <DepthTime>(diveResult.CeilingDepthPoints.Count() / skip);

                var count = diveResult.CeilingDepthPoints.Count();
                for (int i = 0; i < count; i++)
                {
                    if (i % skip == 0)
                    {
                        var elem = diveResult.CeilingDepthPoints.ElementAt(i);
                        ceilingDepthPoints.Add(new DepthTime(elem.Depth, elem.Time));
                    }
                }

                ceilingDepthPoints.Add(new DepthTime(0, diveResult.CeilingDepthPoints.Last().Time));
                plan.CeilingDepthPoints = ceilingDepthPoints;
            }

            var consumedGas = CalculateConsumedGas(diveResult, gasSwitches);

            plan.ConsumedBottomGases = consumedGas.Key;
            plan.ConsumedDecoGases   = consumedGas.Value;
            plan.DiveResultBlocks    = plan.GetDiveInfo();

            return(plan);
        }
예제 #5
0
        private BreathGas SelectDecoGas(DepthFactor currDepth, double decoStopDepth)
        {
            const double MaxDecoPpO     = 1.6;
            const double OptimalDecoPpO = 1.5;

            if ((_diveParameters.DecoLevels?.Count() ?? 0) == 0)
            {
                return(_diveParameters.Levels.Last().Gas);
            }

            var decoPpO = MaxDecoPpO;

            if ((currDepth.Depth - decoStopDepth) > (DecoStopsStep + double.Epsilon))
            {
                decoPpO = OptimalDecoPpO;
            }

            var gasesCanUse = new List <KeyValuePair <double, BreathGas> >(_diveParameters.DecoLevels.Count());

            // explicit depth gases go first
            var explicitDepthGases = _diveParameters.DecoLevels.Where(d => d.Depth > double.Epsilon);

            if (explicitDepthGases.Any())
            {
                var suitedDecoGases = explicitDepthGases.Where(d => currDepth.Depth <= d.Depth);
                if (!suitedDecoGases.Any())
                {
                    return(_diveParameters.Levels.Last().Gas);
                }

                var minDepth        = explicitDepthGases.Where(d => currDepth.Depth <= d.Depth).Min(d => d.Depth);
                var explicitDecoGas = explicitDepthGases.FirstOrDefault(d => DivingMath.CompareDouble(d.Depth, minDepth));

                if (explicitDecoGas != null)
                {
                    return(explicitDecoGas.Gas);
                }
                else
                {
                    return(_diveParameters.Levels.Last().Gas);
                }
            }
            else
            {
                foreach (var decoLevel in _diveParameters.DecoLevels)
                {
                    var currDecoPpO = BreathGas.GetGasPartialPreasureForDepth(currDepth, decoLevel.Gas.PpO);
                    if (currDecoPpO <= decoPpO)
                    {
                        gasesCanUse.Add(new KeyValuePair <double, BreathGas>(currDecoPpO, decoLevel.Gas));
                    }
                }

                if (gasesCanUse.Count == 0)
                {
                    return(_diveParameters.Levels.Last().Gas);
                }

                return(gasesCanUse.First(g => DivingMath.CompareDouble(g.Key, gasesCanUse.Max(k => k.Key))).Value);
            }
        }