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)); }
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; } }
public GasModel(BreathGas gas) { PpO2 = Math.Round(gas.PpO * 100.0, 1); PpHe = Math.Round(gas.PpHe * 100.0, 1); }
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); }
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)); }
public Zhl16Algorithm() { _compartmentTables = new Dictionary <SubType, List <Compartment> >(); //TODO: add helium values _compartmentTables[SubType.A] = new List <Compartment> { new Compartment { N2HalfTime = 4, N2ValueA = 1.2599, N2ValueB = 0.5050 }, new Compartment { N2HalfTime = 8, N2ValueA = 1.0000, N2ValueB = 0.6514 }, new Compartment { N2HalfTime = 12.5, N2ValueA = 0.8618, N2ValueB = 0.7222 }, new Compartment { N2HalfTime = 18.5, N2ValueA = 0.7562, N2ValueB = 0.7725 }, new Compartment { N2HalfTime = 27, N2ValueA = 0.6667, N2ValueB = 0.8125 }, new Compartment { N2HalfTime = 38.3, N2ValueA = 0.5933, N2ValueB = 0.8434 }, new Compartment { N2HalfTime = 54.3, N2ValueA = 0.5282, N2ValueB = 0.8693 }, new Compartment { N2HalfTime = 77, N2ValueA = 0.4701, N2ValueB = 0.8910 }, new Compartment { N2HalfTime = 109, N2ValueA = 0.4187, N2ValueB = 0.9092 }, new Compartment { N2HalfTime = 146, N2ValueA = 0.3798, N2ValueB = 0.9222 }, new Compartment { N2HalfTime = 187, N2ValueA = 0.3497, N2ValueB = 0.9319 }, new Compartment { N2HalfTime = 239, N2ValueA = 0.3223, N2ValueB = 0.9403 }, new Compartment { N2HalfTime = 305, N2ValueA = 0.2971, N2ValueB = 0.9477 }, new Compartment { N2HalfTime = 390, N2ValueA = 0.2737, N2ValueB = 0.9544 }, new Compartment { N2HalfTime = 498, N2ValueA = 0.2523, N2ValueB = 0.9602 }, new Compartment { N2HalfTime = 635, N2ValueA = 0.2327, N2ValueB = 0.9653 } }; //TODO: add helium values _compartmentTables[SubType.B] = new List <Compartment> { new Compartment { N2HalfTime = 4, N2ValueA = 1.2599, N2ValueB = 0.5050 }, //new Compartment { N2HalfTime = 5, N2ValueA = 1.1696, N2ValueB = 0.5578 }, new Compartment { N2HalfTime = 8, N2ValueA = 1.0000, N2ValueB = 0.6514 }, new Compartment { N2HalfTime = 12.5, N2ValueA = 0.8618, N2ValueB = 0.7222 }, new Compartment { N2HalfTime = 18.5, N2ValueA = 0.7562, N2ValueB = 0.7825 }, new Compartment { N2HalfTime = 27, N2ValueA = 0.6667, N2ValueB = 0.8126 }, new Compartment { N2HalfTime = 38.3, N2ValueA = 0.5600, N2ValueB = 0.8434 }, new Compartment { N2HalfTime = 54.3, N2ValueA = 0.4947, N2ValueB = 0.8693 }, new Compartment { N2HalfTime = 77, N2ValueA = 0.4500, N2ValueB = 0.8910 }, new Compartment { N2HalfTime = 109, N2ValueA = 0.4187, N2ValueB = 0.9092 }, new Compartment { N2HalfTime = 146, N2ValueA = 0.3798, N2ValueB = 0.9222 }, new Compartment { N2HalfTime = 187, N2ValueA = 0.3497, N2ValueB = 0.9319 }, new Compartment { N2HalfTime = 239, N2ValueA = 0.3223, N2ValueB = 0.9403 }, new Compartment { N2HalfTime = 305, N2ValueA = 0.2850, N2ValueB = 0.9477 }, new Compartment { N2HalfTime = 390, N2ValueA = 0.2737, N2ValueB = 0.9544 }, new Compartment { N2HalfTime = 498, N2ValueA = 0.2523, N2ValueB = 0.9602 }, new Compartment { N2HalfTime = 635, N2ValueA = 0.2327, N2ValueB = 0.9653 } }; _compartmentTables[SubType.C] = new List <Compartment> { new Compartment { N2HalfTime = 4, N2ValueA = 1.2599, N2ValueB = 0.5050, HeHalfTime = 1.51, HeValueA = 1.7424, HeValueB = 0.4245 }, new Compartment { N2HalfTime = 8, N2ValueA = 1.0000, N2ValueB = 0.6514, HeHalfTime = 3.02, HeValueA = 1.3830, HeValueB = 0.5747 }, new Compartment { N2HalfTime = 12.5, N2ValueA = 0.8618, N2ValueB = 0.7222, HeHalfTime = 4.72, HeValueA = 1.1919, HeValueB = 0.6527 }, new Compartment { N2HalfTime = 18.5, N2ValueA = 0.7562, N2ValueB = 0.7725, HeHalfTime = 6.99, HeValueA = 1.0458, HeValueB = 0.7223 }, new Compartment { N2HalfTime = 27, N2ValueA = 0.6200, N2ValueB = 0.8125, HeHalfTime = 10.21, HeValueA = 0.9220, HeValueB = 0.7582 }, new Compartment { N2HalfTime = 38.3, N2ValueA = 0.5043, N2ValueB = 0.8434, HeHalfTime = 14.48, HeValueA = 0.8205, HeValueB = 0.7957 }, new Compartment { N2HalfTime = 54.3, N2ValueA = 0.4410, N2ValueB = 0.8693, HeHalfTime = 20.53, HeValueA = 0.7305, HeValueB = 0.8279 }, new Compartment { N2HalfTime = 77, N2ValueA = 0.4000, N2ValueB = 0.8910, HeHalfTime = 29.11, HeValueA = 0.6502, HeValueB = 0.8553 }, new Compartment { N2HalfTime = 109, N2ValueA = 0.3750, N2ValueB = 0.9092, HeHalfTime = 41.20, HeValueA = 0.5950, HeValueB = 0.8757 }, new Compartment { N2HalfTime = 146, N2ValueA = 0.3500, N2ValueB = 0.9222, HeHalfTime = 55.19, HeValueA = 0.5545, HeValueB = 0.8903 }, new Compartment { N2HalfTime = 187, N2ValueA = 0.3295, N2ValueB = 0.9319, HeHalfTime = 70.69, HeValueA = 0.5333, HeValueB = 0.8997 }, new Compartment { N2HalfTime = 239, N2ValueA = 0.3065, N2ValueB = 0.9403, HeHalfTime = 90.34, HeValueA = 0.5189, HeValueB = 0.9073 }, new Compartment { N2HalfTime = 305, N2ValueA = 0.2835, N2ValueB = 0.9477, HeHalfTime = 115.29, HeValueA = 0.5181, HeValueB = 0.9122 }, new Compartment { N2HalfTime = 390, N2ValueA = 0.2610, N2ValueB = 0.9544, HeHalfTime = 147.42, HeValueA = 0.5176, HeValueB = 0.9171 }, new Compartment { N2HalfTime = 498, N2ValueA = 0.2480, N2ValueB = 0.9602, HeHalfTime = 188.24, HeValueA = 0.5172, HeValueB = 0.9217 }, new Compartment { N2HalfTime = 635, N2ValueA = 0.2327, N2ValueB = 0.9653, HeHalfTime = 240.03, HeValueA = 0.5119, HeValueB = 0.9267 } }; _airGas = new BreathGas(); var tableSize = _compartmentTables.Values.Max(t => t.Count); _compartmentData = new CompartmentCalculations[tableSize]; _compartmentAltData = new CompartmentCalculations[tableSize]; for (int i = 0; i < _compartmentData.Length; i++) { _compartmentAltData[i] = new CompartmentCalculations(); _compartmentData[i] = new CompartmentCalculations(); } }
private void AddDivePointInfo(double time, double celingDepth, double currDepth, BreathGas gas, bool forceAdd = false) { if (TimeSpan.FromMinutes(time - _trackedDivePointTime).TotalSeconds >= 5 || time <= double.Epsilon || forceAdd) { _mValues.Add(new DepthTime(celingDepth, time)); _divePoints.Add(new DivePoint { DepthFactor = new DepthFactor(currDepth, _waterDensity), CurrentDiveTime = time, CurrentGas = gas }); _trackedDivePointTime = time; } }
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); } }