Skip to content

Instantly share code, notes, and snippets.

@mCzolko
Last active September 7, 2022 21:40
Show Gist options
  • Save mCzolko/a7b7d9179be8f44b55d5a77ac828b3b8 to your computer and use it in GitHub Desktop.
Save mCzolko/a7b7d9179be8f44b55d5a77ac828b3b8 to your computer and use it in GitHub Desktop.
CNS calculation
import kotlin.math.abs
import kotlin.math.log
import kotlin.math.max
import kotlin.math.min
/**
* Shortcuts for the most common operations:
* AAP = absolute atmospheric pressure
* ppO2 = partial pressure of oxygen
* fO2 = oxygen fraction
*/
val cnsTable = mapOf(
0.5..0.6 to Pair(-1800, 1800),
0.6..0.7 to Pair(-1500, 1620),
0.7..0.8 to Pair(-1200, 1410),
0.8..0.9 to Pair(-900, 1170),
0.9..1.1 to Pair(-600, 900),
1.1..1.5 to Pair(-300, 570),
1.5..1.6 to Pair(-750, 1245)
)
val po2lo = cnsTable.keys.map { it.start }
val po2hi = cnsTable.keys.map { it.endInclusive }
val limslp = cnsTable.values.map { it.first }
val limint = cnsTable.values.map { it.second }
fun findCnsTableEntry(ppO2: Double): Map.Entry<ClosedFloatingPointRange<Double>, Pair<Int, Int>>? =
cnsTable.entries.firstOrNull { (range, _) -> ppO2 in range }
fun findCnsTableValue(ppO2: Double): Pair<Int, Int>? {
val found = findCnsTableEntry(ppO2)
if (found != null) {
return found.value
}
return null
}
/**
* @param ppO2 partial pressure of oxygen
* @param minutes minutes of dive
*/
fun cnsFlat(ppO2: Double, minutes: Double): Double {
if (ppO2 < 0.5) {
return 0.0
}
val (slope, intercept) = findCnsTableValue(ppO2) ?: error("ppO2 is too high!")
val timeLimit = slope * ppO2 + intercept
return minutes / timeLimit
}
/**
* @param fO2 oxygen fraction
* @param depth depth in meters
* @param minutes minutes of dive
*/
fun cnsFlat(fO2: Double, depth: Double, minutes: Double): Double {
val aap = (depth / 10) + 1
val ppO2 = fO2 * aap
return cnsFlat(ppO2, minutes)
}
/**
* @param fO2 oxygen fraction
* @param startDepth starting depth in meters
* @param endDepth end depth in meters
* @param rate rate of ascent or descend speed in meters per minute
*/
fun cnsDifference(fO2: Double, startDepth: Double, endDepth: Double, rate: Double): Double {
val sgTime = (endDepth - startDepth) / rate
val startAap = (startDepth / 10) + 1
val endAap = (endDepth / 10) + 1
val maxAap = max(startAap, endAap)
val minAap = min(startAap, endAap)
val maxPpO2 = maxAap * fO2
val minPpO2 = minAap * fO2
if (maxPpO2 <= .5) {
return .0
}
val lowPpO2 = if (minPpO2 < .5) .5 else minPpO2
val o2time = sgTime * (maxPpO2 - lowPpO2) / (maxPpO2 - minPpO2)
val oTime = mutableMapOf<Int, Double>()
val segPpO2 = mutableListOf<Double>()
val ppO2o = mutableListOf<Double>()
val ppO2f = mutableListOf<Double>()
for (i in 0..6) {
if ((maxPpO2 > po2lo[i]) && (lowPpO2 <= po2hi[i])) {
if ((maxPpO2 >= po2hi[i]) && (lowPpO2 < po2lo[i])) {
if (startDepth > endDepth) {
ppO2o.add(i, po2hi[i])
ppO2f.add(i, po2lo[i])
} else {
ppO2o.add(i, po2lo[i])
ppO2f.add(i, po2hi[i])
}
} else if ((maxPpO2 < po2hi[i]) && (lowPpO2 <= po2lo[i])) {
if (startDepth > endDepth) {
ppO2o.add(i, maxPpO2)
ppO2f.add(i, po2lo[i])
} else {
ppO2o.add(i, po2lo[i])
ppO2f.add(i, maxPpO2)
}
} else if ((lowPpO2 > po2lo[i]) && (maxPpO2 >= po2hi[i])) {
if (startDepth > endDepth) {
ppO2o.add(i, po2hi[i])
ppO2f.add(i, lowPpO2)
} else {
ppO2o.add(i, lowPpO2)
ppO2f.add(i, po2hi[i])
}
} else {
if (startDepth > endDepth) {
ppO2o.add(i, maxPpO2)
ppO2f.add(i, lowPpO2)
} else {
ppO2o.add(i, lowPpO2)
ppO2f.add(i, maxPpO2)
}
}
segPpO2.add(i, ppO2f[i] - ppO2o[i])
oTime[i] = o2time * abs(segPpO2[i]) / (maxPpO2 - lowPpO2)
}
}
return oTime.map {
val value = it.value
if (value == .0) {
return@map value
}
val i = it.key
val tLimit = limslp[i] * ppO2o[i] + limint[i]
val mk = limslp[i] * (segPpO2[i] / value)
1.0 / mk * (log(abs(tLimit + mk * value), 2.0) - log(abs(tLimit), 2.0))
}.sumOf { abs(it) }
}
fun main() {
val fO2 = 0.32
val bottomDepth = 33.5
val list = listOf(
cnsDifference(fO2, .0, bottomDepth, 11.0), // descend
cnsFlat(fO2, bottomDepth, 22.0), // bottom
cnsDifference(fO2, bottomDepth, .0, 1.1), // ascend
)
println("pO2: $fO2, depth: 0 -> ${bottomDepth}m, speed: 11.0 m/m -> CNS: ${list[0]}")
println("pO2: $fO2, depth: ${bottomDepth}m, minutes: 22.0 -> CNS: ${list[1]}")
println("pO2: $fO2, depth: ${bottomDepth}m -> 0, speed: 1.1 m/m -> CNS: ${list[2]}")
println("Total CNS: ${list.sum() * 100}%") // result is 27.624661704700294 %
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment