Exemplo n.º 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));
        }
Exemplo n.º 2
0
        private void CalculateCompartmentPresures(double time, double depth, BreathGas gas, CompartmentCalculations[] tissues = null)
        {
            var    depthFact = new DepthFactor(depth, _waterDensity);
            double ppNitro   = gas.PpN * (BreathGas.GetGasPartialPreasureForDepth(depthFact, 1.0) - DivingMath.WaterVapourPreasureBars);
            double ppHe      = gas.PpHe * (BreathGas.GetGasPartialPreasureForDepth(depthFact, 1.0) - DivingMath.WaterVapourPreasureBars);

            int index = 0;

            foreach (var cpt in _compartments)
            {
                var data = (tissues ?? _compartmentData)[index];

                var n2Koef = 1.0 - Math.Pow(2.0, -time / cpt.N2HalfTime);
                var heKoef = 1.0 - Math.Pow(2.0, -time / cpt.HeHalfTime);

                data.N2Presure = data.N2Presure + ((ppNitro - data.N2Presure) * n2Koef);
                data.HePresure = data.HePresure + ((ppHe - data.HePresure) * heKoef);

                ++index;
            }
        }
Exemplo n.º 3
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);
        }
Exemplo n.º 4
0
        public static double CalulateOxygenCns(IEnumerable <DivePoint> divePoints)
        {
            double    cns       = 0;
            DivePoint?prevPoint = null;

            foreach (var point in divePoints)
            {
                if (prevPoint.HasValue)
                {
                    var timeExposure = point.CurrentDiveTime - prevPoint.Value.CurrentDiveTime;
                    var avgDepth     = (prevPoint.Value.DepthFactor.Depth + point.DepthFactor.Depth) * 0.5;
                    var ambPreasure  = BreathGas.GetGasPartialPreasureForDepth(new DepthFactor(avgDepth, point.DepthFactor.WaterDensity), 1.0);
                    var ppO2         = point.CurrentGas.PpO * (ambPreasure - DivingMath.WaterVapourPreasureBars);

                    if (ppO2 >= _cnsTable.Last().PpO)
                    {
                        int ind = -1;
                        for (int i = 1; i < _cnsTable.Length; i++)
                        {
                            if (ppO2 > _cnsTable[i].PpO)
                            {
                                ind = i - 1;
                                break;
                            }
                        }

                        if (ind < 0)
                        {
                            ind = _cnsTable.Length - 1;
                        }

                        var cnsRecord = _cnsTable[ind];
                        var cnsValue  = cnsRecord.Exposure;

                        if (ppO2 > _cnsTable.First().PpO)
                        {
                            cnsValue = _cnsTable.First().Exposure;
                        }
                        else if (ind < (_cnsTable.Length - 1))
                        {
                            var prevRecord = _cnsTable[ind + 1];
                            var deltaPpO2  = ppO2 - prevRecord.PpO;
                            if (deltaPpO2 > 0)
                            {
                                // interpolate
                                var recordsDelta = Math.Abs(cnsRecord.PpO - prevRecord.PpO);
                                var offset       = deltaPpO2 / recordsDelta;

                                cnsValue = offset * cnsRecord.Exposure + (1.0 - offset) * prevRecord.Exposure;
                            }
                        }

                        cns += timeExposure / cnsValue * 100.0;
                    }
                }

                prevPoint = point;
            }


            return(Math.Round(cns, 1));
        }
Exemplo n.º 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);
            }
        }