├── docs ├── NEWS.rst ├── README.rst ├── reference │ ├── cns.pdf │ ├── deepstops.pdf │ ├── m-values_en.pdf │ ├── m-values_fr.pdf │ ├── deco_diy_deco.pdf │ ├── deco_vpm_reinders.pdf │ ├── deco_weinke_rgbm.pdf │ ├── buhlmann_coefs_aab.jpg │ ├── buhlmann_coefs_aab-he.jpg │ ├── GUE_SOP_Manual_Ver2.0.2.pdf │ ├── dessous_de_la_decompression.pdf │ ├── les_modeles_de_decompression.pdf │ ├── repex_tolerating_exposure_to_high_oxygen_levels.pdf │ ├── mf2-elements-de-calcul-pour-l-elaboration-d-un-logiciel-de-decompression.pdf │ └── Modeling_pulmonary_and_CNS_O2_toxicity_and_estimation_of_parameters_for_humans.pdf ├── template.rst ├── index.rst ├── make.bat ├── Makefile ├── autodoc.rst └── conf.py ├── RELEASE-VERSION ├── setup.cfg ├── bin ├── install_develop_tools └── dipplanner ├── .gitignore ├── dipplanner ├── templates │ ├── default.tpl │ ├── default-color.tpl │ ├── base.html │ ├── default.html │ └── base.tpl ├── tests │ ├── dive_repetitive_air_test.json │ ├── dive_repetitive_tx_test.json │ ├── __init__.py │ ├── dive_cc_air_test.json │ ├── dive_cc_txhypo_test.json │ ├── dive_air_test.json │ ├── dive_repetitive_air_test.py │ ├── dive_repetitive_tx_test.py │ ├── dive_txnormo_test.json │ ├── dive_airdeco_test.json │ ├── model_buhlmann_gradient_test.py │ ├── model_buhlmann_oxygen_toxicity_test.py │ ├── dive_txhypo_test.json │ ├── dive_txhypo_forcedtravel_test.json │ ├── configs │ │ ├── restore_default_config.cfg │ │ └── test_config.cfg │ ├── model_buhlmann_compartement_test.py │ ├── segment_test.py │ ├── dive_cc_air_test.py │ ├── dive_cc_txhypo_test.py │ ├── model_buhlmann_model_test.py │ ├── dive_air_test.py │ ├── dive_airdeco_test.py │ └── dive_txnormo_test.py ├── empty.py ├── model │ ├── buhlmann │ │ ├── __init__.py │ │ ├── model_exceptions.py │ │ ├── oxygen_toxicity.py │ │ └── gradient.py │ └── __init__.py ├── __init__.py ├── dipp_exception.py ├── settings.py ├── main.py └── tools.py ├── tests.README ├── configs ├── repetitive_dives.cfg └── default_config.cfg ├── NEWS.rst ├── setup.py └── README.rst /docs/NEWS.rst: -------------------------------------------------------------------------------- 1 | ../NEWS.rst -------------------------------------------------------------------------------- /docs/README.rst: -------------------------------------------------------------------------------- 1 | ../README.rst -------------------------------------------------------------------------------- /RELEASE-VERSION: -------------------------------------------------------------------------------- 1 | 0.3a-21-g257d 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [nosetests] 2 | verbosity=3 3 | with-coverage=1 4 | cover-package=dipplanner 5 | -------------------------------------------------------------------------------- /bin/install_develop_tools: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pip install ipython pylint pep8 nose coverage sphinx 3 | -------------------------------------------------------------------------------- /docs/reference/cns.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/cns.pdf -------------------------------------------------------------------------------- /docs/reference/deepstops.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/deepstops.pdf -------------------------------------------------------------------------------- /docs/reference/m-values_en.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/m-values_en.pdf -------------------------------------------------------------------------------- /docs/reference/m-values_fr.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/m-values_fr.pdf -------------------------------------------------------------------------------- /docs/reference/deco_diy_deco.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/deco_diy_deco.pdf -------------------------------------------------------------------------------- /docs/reference/deco_vpm_reinders.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/deco_vpm_reinders.pdf -------------------------------------------------------------------------------- /docs/reference/deco_weinke_rgbm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/deco_weinke_rgbm.pdf -------------------------------------------------------------------------------- /docs/reference/buhlmann_coefs_aab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/buhlmann_coefs_aab.jpg -------------------------------------------------------------------------------- /docs/reference/buhlmann_coefs_aab-he.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/buhlmann_coefs_aab-he.jpg -------------------------------------------------------------------------------- /docs/reference/GUE_SOP_Manual_Ver2.0.2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/GUE_SOP_Manual_Ver2.0.2.pdf -------------------------------------------------------------------------------- /docs/reference/dessous_de_la_decompression.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/dessous_de_la_decompression.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # use glob syntax. 2 | 3 | *.pyc 4 | *.tmproj 5 | .idea/* 6 | .coverage 7 | *.log 8 | venv/* 9 | build/* 10 | dist/* 11 | _build/* 12 | -------------------------------------------------------------------------------- /docs/reference/les_modeles_de_decompression.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/les_modeles_de_decompression.pdf -------------------------------------------------------------------------------- /docs/reference/repex_tolerating_exposure_to_high_oxygen_levels.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/repex_tolerating_exposure_to_high_oxygen_levels.pdf -------------------------------------------------------------------------------- /docs/reference/mf2-elements-de-calcul-pour-l-elaboration-d-un-logiciel-de-decompression.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/mf2-elements-de-calcul-pour-l-elaboration-d-un-logiciel-de-decompression.pdf -------------------------------------------------------------------------------- /docs/reference/Modeling_pulmonary_and_CNS_O2_toxicity_and_estimation_of_parameters_for_humans.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ThomasChiroux/dipplanner/HEAD/docs/reference/Modeling_pulmonary_and_CNS_O2_toxicity_and_estimation_of_parameters_for_humans.pdf -------------------------------------------------------------------------------- /dipplanner/templates/default.tpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tpl" %} 2 | 3 | {%- macro color(text, color="white") -%} 4 | {{- text -}} 5 | {%- endmacro -%} 6 | 7 | {%- block separator %} 8 | ------------------------------------------------------------------------------- 9 | {%- endblock -%} 10 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_repetitive_air_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "repetitive1": [" 82:07", 40.088097, 14.311420, 6360, 101340, 3708.955573, false], 3 | "repetitive2": [" 84:41", 66.849367, 18.147348, 19260, 127560, 3673.490914, false], 4 | "repetitive3": [" 46:07", 86.882052, 7.757922, 2040, 111240, 2826.291390, true] 5 | } 6 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_repetitive_tx_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "repetitive1": ["138:34", 55.635445, 21.187649, 12840, 62820, 5646.428193, false], 3 | "repetitive2": ["173:03", 86.601005, 24.306663, 19080, 74160, 6651.087671, false], 4 | "repetitive3": [" 86:17", 115.975139, 12.641242, 7620, 71040, 4713.132187, false] 5 | } 6 | -------------------------------------------------------------------------------- /dipplanner/templates/default-color.tpl: -------------------------------------------------------------------------------- 1 | {% extends "base.tpl" %} 2 | 3 | {%- macro color(text, color="white") -%} 4 | {%- if color == "red" %}{{'\033[91m'}}{% endif -%} 5 | {%- if color == "green" %}{{'\033[92m'}}{% endif -%} 6 | {%- if color == "yellow" %}{{'\033[93m'}}{% endif -%} 7 | {%- if color == "blue" %}{{'\033[94m'}}{% endif -%} 8 | {%- if color == "pink" %}{{'\033[95m'}}{% endif -%} 9 | {{- text -}} 10 | {{- '\033[0m'-}} 11 | {%- endmacro -%} 12 | 13 | 14 | {%- block separator %} 15 | {{color("-------------------------------------------------------------------------------","blue")}} 16 | {%- endblock -%} 17 | -------------------------------------------------------------------------------- /tests.README: -------------------------------------------------------------------------------- 1 | To run tests, use the following commands from main module directory : 2 | 3 | Before running tests, you MAY need to setup the develop environment, 4 | eventually using virtualenv. 5 | see README for more infos. 6 | 7 | 8 | Run all tests: 9 | ============== 10 | 11 | nosetests 12 | 13 | 14 | Run one test in particular: 15 | =========================== 16 | 17 | nosetests dipplanner/tests/module_to_test.py 18 | 19 | Run one sub-test in particular: 20 | =============================== 21 | 22 | nosetests dipplanner/tests/module_to_test.py ClassToTest 23 | 24 | ex:: 25 | 26 | nosetests dipplanner/tests/dive_test.py TestDiveAirDiveRunTime1 27 | 28 | Test only rt for a specific module 29 | ================================== 30 | 31 | ex:: 32 | 33 | nosetests -e "tank|flight|otu|cns|desat" dipplanner/tests/dive_air_test.py 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /dipplanner/empty.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Lesser General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Empty module.""" 20 | -------------------------------------------------------------------------------- /dipplanner/model/buhlmann/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Buhlmann Model module.""" 20 | -------------------------------------------------------------------------------- /dipplanner/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Lesser General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Tests.""" 20 | -------------------------------------------------------------------------------- /dipplanner/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Lesser General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Dipplanner module.""" 20 | -------------------------------------------------------------------------------- /dipplanner/model/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright 2011-2012 Thomas Chiroux 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. 18 | # If not, see 19 | # 20 | # This module is part of dipplanner, a Dive planning Tool written in python 21 | """Model module. 22 | 23 | Contains: 24 | * buhlmann model 25 | """ 26 | -------------------------------------------------------------------------------- /docs/template.rst: -------------------------------------------------------------------------------- 1 | .. _dipplanner_templates: 2 | 3 | Templates 4 | ========= 5 | 6 | dipplanner uses Jinja2 template engine. 7 | 8 | For all documentation about jinja2, please see `their own documentation `_ 9 | 10 | This document will focus on dipplanner objects sent to jinja2 templating system and 11 | how to use them. 12 | 13 | dipplanner sends two objects to the template engine: 14 | 15 | * settings: contains all the parameters used for the dives 16 | * [Dive, ...]: a list of Dive objects 17 | 18 | It only one dive is calculated, dipplanner still send a list of dive, with one element in the list. 19 | 20 | Settings will be usefull to display some dive parameters, like configured GF for example: 21 | 22 | :: 23 | 24 | Configuration : GF:{{ settings.GF_LOW*100 }}-{{ settings.GF_HIGH*100 }} 25 | 26 | dives 27 | ----- 28 | 29 | Because dipplanner sends a list of Dives, the template MUST iterate this 30 | list, event for one element : 31 | 32 | :: 33 | 34 | {% for dive in dives %} 35 | 36 | ... 37 | 38 | {% endfor %} 39 | 40 | Dive attributes are described here: :ref:`dipplanner_autodoc_dive` 41 | 42 | settings 43 | -------- 44 | 45 | settings attributes are described here: :ref:`dipplanner_autodoc_settings` -------------------------------------------------------------------------------- /bin/dipplanner: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright 2011 Thomas Chiroux 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as 8 | # published by the Free Software Foundation, either version 3 of the 9 | # License, or (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. 18 | # If not, see 19 | # 20 | # This module is part of dipplanner, a Dive planning Tool written in python 21 | # Strongly inspired by Guy Wittig's MVPlan 22 | """main dipplanner executable 23 | runs in command line and output resulting dive profile 24 | also initiate log files 25 | """ 26 | __authors__ = [ 27 | # alphabetical order by last name 28 | 'Thomas Chiroux', ] 29 | 30 | 31 | import sys 32 | 33 | # local imports 34 | from dipplanner import main 35 | 36 | 37 | if __name__ == "__main__": 38 | main.main() 39 | -------------------------------------------------------------------------------- /dipplanner/dipp_exception.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Base Class for exceptions for dipplanner module.""" 20 | 21 | import logging 22 | 23 | 24 | class DipplannerException(Exception): 25 | """Base exception class for dipplanner.""" 26 | 27 | def __init__(self, description): 28 | """Init of DipplannerException. 29 | 30 | :param str description: text describing the error 31 | """ 32 | super().__init__() 33 | self.logger = logging.getLogger(self.__class__.__name__) 34 | self.description = description 35 | 36 | def __str__(self): 37 | """String representing the object. 38 | 39 | :returns: a string describing the Exception 40 | :rtype: str 41 | """ 42 | return ''.join(self.description) 43 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. dipplanner documentation master file, created by 2 | sphinx-quickstart on Wed Aug 15 00:20:57 2012. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to dipplanner's documentation! 7 | ====================================== 8 | 9 | .. warning:: 10 | 11 | This software is provided free of charge for experienced divers to evaluate diving profiles. 12 | This software is highly experimental, it probably contains bugs and should not be relied upon for actual dives. Use it at your own risk. 13 | 14 | If you follow dive schedules generated by this software you could suffer decompression sickness, serious injury or possibly death. 15 | The author does not warrant that this software accurately reflects Buhlmann's algorithms, or that it will produce safe, reliable, results. 16 | 17 | Diving in general is fraught with risk, and decompression diving using mixed gases and rebreathers adds significantly more risk. This software is not intended for uneducated users. 18 | This software and the decompression schedules it produces are tools for experienced divers only. 19 | 20 | IF YOU DO NOT UNDERSTAND OR DO NOT AGREE TO THIS STATEMENT DO NOT USE THIS SOFTWARE 21 | 22 | .. toctree:: 23 | :maxdepth: 4 24 | 25 | README 26 | command_line 27 | config_file 28 | template 29 | autodoc 30 | NEWS 31 | 32 | TODOs 33 | ===== 34 | 35 | .. todolist:: 36 | 37 | 38 | Indices and tables 39 | ================== 40 | 41 | * :ref:`genindex` 42 | * :ref:`modindex` 43 | * :ref:`search` 44 | 45 | -------------------------------------------------------------------------------- /configs/repetitive_dives.cfg: -------------------------------------------------------------------------------- 1 | # dipplanner config file 2 | # this file is used by the command line tool and 3 | # override the defaults parameters or input some dive profiles 4 | 5 | # =============================== dive profiles ================================ 6 | # repetitive dives are given using [diveXXX] section, where XXX represent a 7 | # number. 8 | # the dives will be done in croissant order. 9 | 10 | [dive1] 11 | 12 | # Tank list for this dive: 13 | # Format: tankXXX=tank_name;fO2;fHe;Volume(l);Pressure(bar) 14 | tank1 = airtank;0.21;0.0;15;230;50b 15 | 16 | # segment list for this dive. At least ONE segment is mandatory 17 | # Format: segmentXXX=depth(m);duration(s);tank_name;set_point(for ccr) 18 | segment1 = 30;20*60;airtank;0.0 19 | 20 | [dive2] 21 | # surface_interval (in seconds) 22 | # for repetitive dives, you can specify the surface time between the previous 23 | # dive and this dive 24 | surface_interval = 60*60 25 | 26 | # Tanks 27 | # see dive 1 for more explanation 28 | # tank list is not mandatory for repetitive dives : if not given 29 | # last dive tanks will be used. 30 | # if 'automatic_tank_refill' is set to True, the tank will be full before the 31 | # dive. If set to False, it'll use the remaining gas from last dive 32 | # If at least ONE tank is provided for a repetitive dive, ALL the Tank MUST 33 | # be specified 34 | # newtank = txtank;0.21;0.30;15;230;50b 35 | # tank1 = airtank;0.21;0.0;15;230;50b 36 | 37 | # segment list for this dive. At least ONE segment is mandatory 38 | segment1 = 20;30*60;airtank;0.0 39 | 40 | #[dive3]... 41 | 42 | [general] 43 | automatic_tank_refill = true 44 | 45 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_cc_air_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "10:10": [" 11:30", 13.437123, 4.834078, 0, 60, 0.000000, true], 3 | "10:20": [" 21:30", 26.673628, 9.595983, 0, 60, 0.000000, true], 4 | "10:30": [" 31:30", 39.910132, 14.357888, 0, 60, 0.000000, true], 5 | "10:40": [" 41:30", 53.146637, 19.119793, 0, 60, 0.000000, true], 6 | "10:50": [" 51:30", 66.383141, 23.881697, 0, 60, 0.000000, true], 7 | "10:60": [" 61:30", 79.619646, 28.643602, 0, 120, 0.000000, true], 8 | "10:70": [" 71:30", 92.856151, 33.405507, 0, 120, 0.000000, true], 9 | "20:10": [" 13:00", 13.637741, 4.906252, 420, 10680, 0.000000, true], 10 | "20:20": [" 23:43", 26.989929, 9.709774, 0, 21300, 0.000000, true], 11 | "20:30": [" 33:43", 40.226433, 14.471679, 0, 31860, 0.000000, true], 12 | "20:40": [" 43:43", 53.462938, 19.233584, 360, 42600, 0.000000, true], 13 | "20:50": [" 54:26", 66.815125, 24.037106, 840, 52620, 0.000000, true], 14 | "30:10": [" 15:56", 14.069725, 5.061660, 60, 21540, 0.000000, true], 15 | "30:20": [" 27:45", 28.877928, 10.388992, 720, 42000, 0.000000, true], 16 | "30:30": [" 41:53", 46.752588, 16.819499, 1680, 59220, 0.000000, true], 17 | "30:40": [" 57:40", 67.644204, 24.335372, 2460, 71400, 0.000000, true], 18 | "30:50": [" 73:40", 88.822612, 31.954419, 3300, 81480, 0.000000, true], 19 | "40:10": [" 20:50", 16.271955, 5.853925, 300, 31500, 0.000000, true], 20 | "40:20": [" 38:34", 38.911757, 13.998717, 1680, 59040, 0.000000, true], 21 | "40:30": [" 60:37", 67.265316, 24.199064, 2880, 76680, 0.000000, true], 22 | "40:40": [" 83:12", 97.157755, 34.953033, 4380, 90120, 0.000000, true], 23 | "40:50": ["106:20", 127.778203, 45.968906, 6420, 100800, 0.000000, true], 24 | "multilevel1": [" 68:07", 77.966132, 28.048742, 3480, 82560, 0.000000, true] 25 | } 26 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_cc_txhypo_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "100:10": [" 86:48", 82.051486, 29.518470, 4740, 45720, 0.000000, true], 3 | "100:15": ["133:27", 142.133913, 51.133451, 9420, 53400, 0.000000, true], 4 | "100:20": ["183:42", 207.814415, 74.762370, 14100, 58140, 0.000000, true], 5 | "100:5": [" 46:33", 31.273355, 11.250760, 1500, 31260, 0.000000, true], 6 | "50:10": [" 29:48", 24.689964, 8.882349, 1020, 26100, 0.000000, true], 7 | "50:20": [" 59:23", 62.182090, 22.370346, 2820, 40260, 0.000000, true], 8 | "50:30": [" 91:01", 104.053566, 37.433838, 5700, 48240, 0.000000, true], 9 | "50:40": ["124:20", 147.320253, 52.999265, 8520, 53100, 0.000000, true], 10 | "50:50": ["159:23", 193.714202, 69.689741, 11940, 56640, 0.000000, true], 11 | "60:10": [" 38:40", 32.975607, 11.863155, 1440, 31140, 0.000000, true], 12 | "60:20": [" 77:51", 83.174777, 29.922580, 4440, 45240, 0.000000, true], 13 | "60:30": ["120:45", 139.126449, 50.051499, 8340, 52620, 0.000000, true], 14 | "60:40": ["165:41", 197.769542, 71.148672, 12660, 57120, 0.000000, true], 15 | "70:10": [" 49:00", 43.202604, 15.542373, 2040, 35520, 0.000000, true], 16 | "70:20": [" 99:23", 107.393726, 38.635479, 6360, 49200, 0.000000, true], 17 | "70:30": ["155:26", 180.751401, 65.026303, 11880, 56220, 0.000000, true], 18 | "80:10": [" 60:32", 54.185049, 19.493367, 2700, 39120, 0.000000, true], 19 | "80:20": ["124:32", 137.232811, 49.370253, 8640, 52800, 0.000000, true], 20 | "80:30": ["196:13", 231.283555, 83.205521, 14880, 59160, 0.000000, true], 21 | "90:10": [" 72:33", 66.640191, 23.974172, 3660, 42660, 0.000000, true], 22 | "90:20": ["152:03", 169.371602, 60.932358, 11520, 55620, 0.000000, true], 23 | "90:30": ["241:54", 287.468663, 103.418420, 16920, 61320, 0.000000, true], 24 | "multilevel1": [" 79:04", 90.734743, 32.642319, 4380, 45660, 0.000000, true] 25 | 26 | } 27 | -------------------------------------------------------------------------------- /dipplanner/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% block head %} 5 | 16 | Dipplanner v{{ settings.__VERSION__ }} {% block title %}{% endblock %} 17 | {% endblock %} 18 | 19 | 20 |
21 | {% block dive_header %} 22 |

Dipplanner v{{ settings.__VERSION__ }}

23 | {% endblock %} 24 | 25 | {% block dive_content %} 26 | {% endblock %} 27 | 28 | {% block dive_footer %} 29 |

WARNING

30 | 31 |

This software is provided free of charge for experienced divers to evaluate diving profiles. 32 | This software is highly experimental, it probably contains bugs and should not be relied upon for actual dives. Use it at your own risk.

33 | 34 |

If you follow dive schedules generated by this software you could suffer decompression sickness, serious injury or possibly death. 35 | The author does not warrant that this software accurately reflects Buhlmann's algorithms, or that it will produce safe, reliable, results.

36 | 37 |

Diving in general is fraught with risk, and decompression diving using mixed gases and rebreathers adds significantly more risk. This software is not intended for uneducated users. 38 | This software and the decompression schedules it produces are tools for experienced divers only.

39 | 40 |

IF YOU DO NOT UNDERSTAND OR DO NOT AGREE TO THIS STATEMENT DO NOT USE THIS SOFTWARE

41 | {% endblock %} 42 |
43 | 48 | 49 | -------------------------------------------------------------------------------- /dipplanner/model/buhlmann/model_exceptions.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2012 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Define Exceptions for buhlmann model.""" 20 | from dipplanner.dipp_exception import DipplannerException 21 | 22 | 23 | class ModelException(DipplannerException): 24 | """Generic Model Exception.""" 25 | 26 | def __init__(self, description): 27 | """Init of ModelException. 28 | 29 | :param str description: text describing the error 30 | """ 31 | super().__init__(description) 32 | self.logger.error( 33 | "Raising an exception: ModelException ! (%s)", description) 34 | 35 | 36 | class ModelStateException(ModelException): 37 | """Model State Exception.""" 38 | 39 | def __init__(self, description): 40 | """Init of ModelStateException. 41 | 42 | :param str description: text describing the error 43 | """ 44 | super().__init__(description) 45 | self.logger.error( 46 | "Raising an exception: ModelStateException ! (%s)", description) 47 | 48 | 49 | class ModelValidationException(ModelException): 50 | """Model State Exception.""" 51 | 52 | def __init__(self, description): 53 | """Init of ModelValidationException. 54 | 55 | :param str description: text describing the error 56 | """ 57 | super().__init__(description) 58 | self.logger.error( 59 | "Raising an exception: ModelValidationException ! (%s)", 60 | description) 61 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_air_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "10:10": [" 11:00", 0.000000, 0.000000, 120, 7920, 430.053150, true], 3 | "10:20": [" 21:00", 0.000000, 0.000000, 1560, 16140, 834.789150, true], 4 | "10:30": [" 31:43", 0.000000, 0.000000, 0, 24240, 1256.205527, true], 5 | "10:40": [" 41:43", 0.000000, 0.000000, 0, 32520, 1660.941527, true], 6 | "10:50": [" 51:43", 0.000000, 0.000000, 0, 40620, 2065.677527, true], 7 | "10:60": [" 61:43", 0.000000, 0.000000, 360, 48780, 2470.413527, false], 8 | "10:70": [" 71:43", 0.000000, 0.000000, 720, 55800, 2875.149527, false], 9 | "20:10": [" 12:43", 3.019300, 1.586703, 0, 15300, 684.240977, true], 10 | "20:20": [" 23:26", 6.361822, 3.341089, 180, 31920, 1312.073047, true], 11 | "20:30": [" 34:58", 9.704344, 5.095475, 1020, 48840, 1962.510537, true], 12 | "20:40": [" 49:50", 13.046867, 6.849861, 1860, 63180, 2679.786918, false], 13 | "20:50": [" 69:49", 16.389389, 8.604247, 2760, 75240, 3525.909385, false], 14 | "30:10": [" 15:09", 6.340065, 2.412164, 120, 22200, 978.202559, true], 15 | "30:20": [" 31:12", 13.705485, 5.189942, 1080, 49020, 1949.014692, true], 16 | "30:30": [" 57:11", 21.072420, 7.970035, 2520, 71580, 3178.312962, false], 17 | "30:40": [" 89:11", 28.437840, 10.747813, 4020, 88440, 4571.273543, false], 18 | "30:50": ["125:09", 35.982045, 13.798738, 6540, 101760, 6105.478630, false], 19 | "40:10": [" 18:13", 9.000305, 3.434155, 240, 28620, 1297.344919, true], 20 | "40:20": [" 46:07", 20.032685, 7.687034, 2040, 65220, 2826.291390, false], 21 | "40:30": [" 90:46", 31.399551, 12.213941, 4320, 90240, 4802.159987, false], 22 | "40:40": ["142:42", 43.333578, 17.075905, 8580, 108000, 7016.516703, false], 23 | "40:50": ["196:34", 54.967717, 21.768918, 14940, 120360, 9300.536763, false], 24 | "50:10": [" 23:17", 11.198793, 4.351139, 420, 35160, 1705.956563, true], 25 | "50:20": [" 67:19", 26.153779, 10.397083, 3000, 78960, 3919.698843, false], 26 | "50:30": ["132:24", 42.205359, 16.950032, 7680, 105480, 6760.602756, false], 27 | "50:40": ["208:15", 59.467531, 24.082781, 16500, 123240, 9952.917347, false], 28 | "50:50": ["294:21", 76.240844, 31.123310, 26880, 135060, 13419.288012, false], 29 | "60:10": [" 27:56", 13.044585, 6.181109, 720, 41400, 2114.771341, true], 30 | "60:20": [" 91:08", 32.396348, 15.577201, 4380, 90600, 5152.851272, false], 31 | "60:25": ["135:25", 43.292968, 20.782976, 7980, 106560, 7055.424839, false], 32 | "60:30": ["180:56", 53.843507, 25.942999, 13680, 118080, 9027.476626, false], 33 | "multilevel1": ["117:28", 37.825321, 14.831368, 6300, 100800, 6091.263643, false] 34 | } 35 | -------------------------------------------------------------------------------- /NEWS.rst: -------------------------------------------------------------------------------- 1 | Release Notes 2 | ============= 3 | 4 | v0.3 (ongoing) 5 | -------------- 6 | 7 | .. note:: 8 | v0.3 is not released yet. 9 | 10 | Below are the feature already developed for the 0.3 target release. 11 | 12 | See GitHub Issues for more infos. 13 | 14 | Python 15 | ****** 16 | 17 | * dipplanner is now (only) python 3.4+ compatible 18 | now old python 2.7 compatible version is frozen in py2.7 branch. 19 | 20 | New features 21 | ************ 22 | 23 | * No-flight time calculation 24 | * non-blocking dive situations errors 25 | 26 | Bug corrections 27 | *************** 28 | 29 | * tank infos on repetitive dives was wrong (only last tank status was 30 | diven, even for the firs(s) dive(s). 31 | 32 | v0.2 33 | ---- 34 | 35 | New features 36 | ************ 37 | 38 | * New submodel: buhlmann ZH-L16C 39 | * Handle repetitive dives 40 | * Variable ppH2O in surface based on Temp and % humidity 41 | * Limit CCR dive depth based on diluant values 42 | * Export dive plannification in different format 43 | * Config Files 44 | * Tanks: 45 | * More accurate calculation of tank volume 46 | * minimum gas remaining rules in tanks 47 | * handle double tanks 48 | 49 | Bug corrections 50 | *************** 51 | 52 | * Automatic gas selection was not well handled on Hypoxic trimix dives 53 | 54 | v0.1 55 | ---- 56 | 57 | New features 58 | ************ 59 | 60 | * Same 'base' functionnality as MVPlan (v1.5): 61 | * buhlmann ZH-L16B 62 | * mv-value gradient conservatism (adjustable) 63 | * oxygen toxicity (OTU and CNS) calculation 64 | * support Open Circuit dives : air, nitrox, trimix, heliox... 65 | * support CCR dives with OC deco/bailout if wanted 66 | * support dive in altitude 67 | * automatic selection of deco when ascending 68 | * ... 69 | * But some differences: 70 | * command line only (or direct use in python) : no GUI, no automatic update 71 | * only in english 72 | * the unit system is only SI: 73 | * meter 74 | * seconds 75 | * bar (almost SI: 1 bar = 10E5 pascal) 76 | * liter (dm3) 77 | * Uses 'Tanks' instead of 'Gases' in order to calculate gas consumption for each tank and raise error when dive reach empty tank 78 | * All time parameters and calculations are by default in seconds instead of minutes 79 | * the END 'Equivalent Narcosis depth' is calculated based on narcosis index of every breathed gases (N2, He and O2) instead of the 'Only N2 is narcotic' in MVPlan 80 | * water pressure is by default calculated using the 'real pressure' of water (wich can change between fresh or sea water) instead of '10m == 1bar' 81 | -------------------------------------------------------------------------------- /dipplanner/templates/default.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% macro color(text, color="white") %} 4 | {{ text }} 5 | {% endmacro %} 6 | 7 | {% block dive_content %} 8 | {%- for dive in dives -%} 9 | 10 | {% block separator %} 11 |
12 | {% endblock %} 13 | 14 |

Configuration : GF:{{ settings.GF_LOW*100 }}-{{ settings.GF_HIGH*100 }}

15 | {%- if dive.is_repetitive_dive -%} 16 |

{{- color(" - Repetitive dive - surface interval: %s mins" % dive.get_surface_interval(), "yellow") }}

17 | {%- endif -%} 18 | 19 | {{ self.separator() }} 20 | 21 | {% for exc in dive.dive_exceptions %} 22 |

{{ color("Exception: ", "red")-}} 23 | {{ color(exc.__repr__(), "red") -}} 24 | : {{ color(exc.description, "red") }}

25 | {%- endfor -%} 26 | 27 | {% for segment in dive.output_segments %} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {% if segment.end > settings.DEFAULT_MAX_END %} 36 | 37 | {% else %} 38 | 39 | {% endif %} 40 | 41 | {% endfor %} 42 |
{{ "%8s"|format(segment.type|upper) }}{{ " at %3d"|format(segment.depth|int) }}mfor {{ segment.segment_time_str }}[RT:{{ segment.run_time_str }}]on {{ " %12s"|format(segment.tank|string) }}SP:{{ segment.setpoint }}{{color(" END:%im"|format(segment.end), "red") }}{{ " END:%im"|format(segment.end) }}
43 | 44 | {{ self.separator() }} 45 | 46 |

Gas:

47 | 48 | {% for tank in dive.tanks %} 49 | 50 | 51 | 52 | 53 | 54 | {% if not tank.check_rule() %} 55 | 56 | 58 | {% else %} 59 | 60 | {% endif %} 61 | {% endfor %} 62 |
{{ " %12s : "|format(tank|string) }}Total: {{ "%6.1f"|format(tank.total_gas) }}lUsed: {{ "%6.1fl"|format(tank.used_gas) }}{{ " (rem:%6.1fl or "|format(tank.remaining_gas) }} {{ "%db"|format(tank.remaining_gas / tank.tank_vol) }})
{{color(" WARNING !!! Not enought remaining gas in the %s tank (min:" % tank, "red") }} 57 | {{ color("%6.1fl) !"|format(tank.min_gas),"red") }}
63 | 64 | {{ self.separator() }} 65 | 66 | Oxygen Toxicity: OTU:{{ dive.model.ox_tox.otu|int }}, CNS: 67 | {{ "%d"|format(dive.model.ox_tox.cns*100)}}% 68 | 69 | {% if dive.no_flight_time_value %} 70 | {{ self.separator() }} 71 | No-flight time: {{ dive.get_no_flight_hhmmss() }} 72 | {{ self.separator() }} 73 | {% endif %} 74 | 75 | {{ self.separator() }} 76 | {% endfor %} 77 | {% endblock %} 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /dipplanner/templates/base.tpl: -------------------------------------------------------------------------------- 1 | {%- block dive_header -%} 2 | Dipplanner v{{ settings.__VERSION__ }} 3 | {% endblock -%} 4 | 5 | {%- for dive in dives -%} 6 | 7 | {%- block separator -%}{%- endblock %} 8 | Configuration : GF:{{ settings.GF_LOW*100 }}-{{ settings.GF_HIGH*100 }} 9 | {%- if dive.is_repetitive_dive -%} 10 | {{- color(" - Repetitive dive - surface interval: %s mins" % dive.get_surface_interval(), "yellow") }} 11 | {%- endif -%} 12 | 13 | {{ self.separator() }} 14 | {%- for exc in dive.dive_exceptions %} 15 | {{ color("Exception: ", "red")-}} 16 | {{ color(exc.__repr__(), "red") -}} 17 | : {{ color(exc.description, "red") }} 18 | {%- endfor -%} 19 | 20 | {%- for segment in dive.output_segments %} 21 | {{ "%8s"|format(segment.type|upper) -}}: 22 | {{- " at %3d"|format(segment.depth|int) -}}m for 23 | {{- segment.segment_time_str }} [RT:{{ segment.run_time_str -}}], on 24 | {{- " %12s"|format(segment.tank|string) }}, SP:{{ segment.setpoint -}}, 25 | {%- if segment.end > settings.DEFAULT_MAX_END -%} 26 | {{-color(" END:%im"|format(segment.end), "red") -}} 27 | {%- else -%} 28 | {{- " END:%im"|format(segment.end) -}} 29 | {%- endif -%} 30 | {%- endfor -%} 31 | 32 | {{ self.separator() }} 33 | Gas: {% for tank in dive.tanks %} 34 | {{ " %12s"|format(tank|string) -}}: Total: {{- "%6.1f"|format(tank.total_gas) -}}l, Used: 35 | {{- "%6.1fl"|format(tank.used_gas) -}} 36 | {{- " (rem:%6.1fl or "|format(tank.remaining_gas) -}} 37 | {{- "%db"|format(tank.remaining_gas / tank.tank_vol) -}}) 38 | {%- if not tank.check_rule() %} 39 | {{color(" WARNING !!! Not enought remaining gas in the %s tank (min:" % tank, "red") -}} 40 | {{- color("%6.1fl) !"|format(tank.min_gas),"red") -}} 41 | {%- endif -%} 42 | {%- endfor -%} 43 | 44 | {{ self.separator() }} 45 | Oxygen Toxicity: OTU:{{ dive.model.ox_tox.otu|int }}, CNS: 46 | {{- "%d"|format(dive.model.ox_tox.cns*100)}}% 47 | {{- self.separator() }} 48 | No-flight time: {{ dive.get_no_flight_hhmmss() -}} {{ " " * 5 }} - {{ " " * 5 -}} 49 | Full desat: {{ dive.get_full_desat_hhmmss() }} 50 | {{- self.separator() }} 51 | Tissues saturations 52 | +---------+---------+---------+---------+---------+---------+ 53 | {% for comp in dive.model.tissues %} 54 | {{- "x" * ((comp.pp_n2 - 0.78)* 30)|round(method='ceil')|int}}{{"o" * (comp.pp_he * 30)|round(method='ceil')|int}} 55 | {% endfor -%} 56 | +---------+---------+---------+---------+---------+---------+ 57 | x: N2 ----- o: He 58 | {% endfor -%} 59 | 60 | {%- block dive_footer %} 61 | WARNING : This software is highly experimental and 62 | must not be used for actual dives. Use it at your own risk. 63 | {%- endblock -%} 64 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_repetitive_air_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test repetitive Dives with air.""" 23 | import json 24 | import pkg_resources 25 | 26 | from dipplanner.tests.common import TestDive, TMethodsMixin 27 | 28 | 29 | class TestDiveAirBase(TestDive): 30 | """Class for test air dive.""" 31 | 32 | def setUp(self): 33 | """Init of the tests.""" 34 | super().setUp() 35 | 36 | # load json results file 37 | self.results = json.loads( 38 | pkg_resources.resource_string( 39 | "dipplanner.tests", 40 | __name__.split('.')[-1] + '.json').decode('utf-8')) 41 | 42 | 43 | class TestDiveAir(TestDiveAirBase): 44 | """Class for test air dive.""" 45 | 46 | def setUp(self): 47 | """Init of the tests.""" 48 | super().setUp() 49 | 50 | self.dive_tank = self.airtank 51 | self.all_tanks = [self.airtank] 52 | self.do_repetitive_dive() 53 | 54 | 55 | # ============================================================================= 56 | # ======= S Y S T E M A T I C T E S T S ================================ 57 | # ============================================================================= 58 | 59 | # AIR ========================================================================= 60 | class TestRepetitiveDive1(TestDiveAir, TMethodsMixin): 61 | """Repetitive dive test 1.""" 62 | 63 | params = ((40, 20, 0), (40, 20, 20)) 64 | name = 'repetitive1' 65 | 66 | 67 | class TestRepetitiveDive2(TestDiveAir, TMethodsMixin): 68 | """Repetitive dive test 2.""" 69 | 70 | params = ((40, 20, 0), (30, 40, 30), (25, 35, 60)) 71 | name = 'repetitive2' 72 | 73 | 74 | class TestRepetitiveDive3(TestDiveAir, TMethodsMixin): 75 | """Repetitive dive test 3.""" 76 | 77 | params = ((40, 20, 0), (30, 40, 30), (25, 35, 60), (40, 20, 12 * 60)) 78 | name = 'repetitive3' 79 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_repetitive_tx_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test repetitive Dives with trimix.""" 23 | import json 24 | import pkg_resources 25 | 26 | from dipplanner.tests.common import TestDive, TMethodsMixin 27 | 28 | 29 | class TestDiveTxBase(TestDive): 30 | """Class for test air dive.""" 31 | 32 | def setUp(self): 33 | """Init of the tests.""" 34 | super().setUp() 35 | 36 | # load json results file 37 | self.results = json.loads( 38 | pkg_resources.resource_string( 39 | "dipplanner.tests", 40 | __name__.split('.')[-1] + '.json').decode('utf-8')) 41 | 42 | 43 | class TestDiveTx(TestDiveTxBase): 44 | """Class for test air dive.""" 45 | 46 | def setUp(self): 47 | """Init of the tests.""" 48 | super().setUp() 49 | 50 | self.dive_tank = self.txtank1 51 | self.all_tanks = [self.txtank1] 52 | self.do_repetitive_dive() 53 | 54 | 55 | # ============================================================================= 56 | # ======= S Y S T E M A T I C T E S T S ================================ 57 | # ============================================================================= 58 | 59 | # Tx Normo ==================================================================== 60 | class TestRepetitiveTxDive1(TestDiveTx, TMethodsMixin): 61 | """Repetitive tx dive test 1.""" 62 | 63 | params = ((55, 20, 0), (50, 20, 20)) 64 | name = 'repetitive1' 65 | 66 | 67 | class TestRepetitiveTxDive2(TestDiveTx, TMethodsMixin): 68 | """Repetitive tx dive test 2.""" 69 | 70 | params = ((55, 20, 0), (50, 20, 30), (35, 35, 60)) 71 | name = 'repetitive2' 72 | 73 | 74 | class TestRepetitiveTxDive3(TestDiveTx, TMethodsMixin): 75 | """Repetitive tx dive test 3.""" 76 | 77 | params = ((55, 20, 0), (50, 20, 30), (35, 35, 60), (55, 20, 12 * 60)) 78 | name = 'repetitive3' 79 | 80 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_txnormo_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "10:10": [" 11:00", 0.133348, 0.072254, 600, 5040, 30.369300, true, 399.683850, true], 3 | "10:20": [" 21:43", 0.212421, 0.102775, 0, 10260, 47.049677, true, 804.419850, true], 4 | "10:30": [" 31:43", 0.212421, 0.102775, 0, 15540, 47.049677, true, 1209.155850, true], 5 | "10:40": [" 41:43", 0.212421, 0.102775, 120, 20760, 47.049677, true, 1613.891850, true], 6 | "10:50": [" 51:43", 0.212421, 0.102775, 420, 25140, 47.049677, true, 2018.627850, true], 7 | "10:60": [" 61:43", 0.212421, 0.102775, 720, 28680, 47.049677, true, 2423.363850, true], 8 | "10:70": [" 71:43", 0.212421, 0.102775, 1020, 31620, 47.049677, true, 2828.099850, true], 9 | "20:10": [" 13:26", 3.326470, 1.709964, 0, 7380, 64.113430, true, 641.137617, true], 10 | "20:20": [" 23:26", 6.668992, 3.464350, 420, 15420, 64.113430, true, 1247.959617, true], 11 | "20:30": [" 35:32", 11.637293, 5.891104, 1140, 23160, 120.410038, true, 1854.781617, true], 12 | "20:40": [" 48:39", 18.576637, 9.039008, 1680, 28800, 193.677534, true, 2461.603617, true], 13 | "20:50": [" 62:05", 26.202040, 12.443857, 2340, 33000, 278.669410, true, 3068.425617, true], 14 | "30:10": [" 15:09", 6.793639, 2.631404, 180, 10560, 89.453192, true, 888.749367, true], 15 | "30:20": [" 29:39", 18.912924, 7.955478, 1020, 20700, 183.421079, true, 1728.588089, true], 16 | "30:30": [" 47:03", 34.077757, 15.193904, 1860, 29460, 337.192778, true, 2593.567416, true], 17 | "30:40": [" 66:06", 49.755754, 22.476312, 3060, 38580, 500.026375, true, 3503.575612, true], 18 | "30:50": [" 85:45", 65.787192, 30.860571, 4620, 48240, 665.088227, true, 4447.800213, true], 19 | "40:10": [" 18:48", 10.898068, 4.213580, 360, 13680, 118.569802, true, 1210.134019, true], 20 | "40:20": [" 39:30", 31.797569, 14.401644, 1500, 27660, 311.231734, true, 2356.339026, true], 21 | "40:30": [" 64:07", 55.633750, 25.854477, 3120, 41220, 556.269822, true, 3588.782868, true], 22 | "40:40": [" 91:34", 80.690974, 38.291855, 5520, 54360, 815.159614, true, 4915.188124, true], 23 | "40:50": ["118:53", 106.308251, 51.304069, 8160, 63540, 1092.575603, true, 6212.728329, false], 24 | "50:10": [" 22:14", 15.029240, 6.635194, 540, 16500, 155.595165, true, 1529.526516, true], 25 | "50:20": [" 51:05", 44.718384, 20.671561, 2220, 35220, 443.316876, true, 3090.517079, true], 26 | "50:30": [" 83:45", 78.131576, 37.890081, 4980, 53340, 785.952323, true, 4746.545310, true], 27 | "50:40": ["122:44", 115.370323, 56.291568, 8820, 66840, 1186.428176, true, 6567.272428, false], 28 | "50:50": ["161:19", 152.044072, 74.944524, 13140, 76320, 1585.807343, false, 8380.418541, false], 29 | "60:10": [" 26:41", 18.568976, 9.277572, 720, 18900, 188.466149, true, 1925.074038, true], 30 | "60:20": [" 63:11", 58.171105, 30.248464, 3060, 43020, 584.289746, true, 3883.994098, true], 31 | "60:25": [" 83:40", 79.402428, 42.003637, 4980, 54120, 801.497125, true, 4945.421444, true], 32 | "60:30": ["107:25", 102.727521, 54.381171, 7560, 63420, 1047.852939, true, 6082.100469, false], 33 | "multilevel1": [" 78:35", 68.622390, 32.661719, 4380, 49260, 684.159172, true, 4425.303497, true] 34 | } 35 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_airdeco_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "10:10": [" 11:00", 0.133348, 0.072254, 120, 7800, 30.369300, true, 399.683850, true], 3 | "10:20": [" 21:00", 0.133348, 0.072254, 1500, 16020, 30.369300, true, 804.419850, true], 4 | "10:30": [" 31:43", 0.212421, 0.102775, 0, 24000, 47.049677, true, 1209.155850, true], 5 | "10:40": [" 41:43", 0.212421, 0.102775, 0, 32280, 47.049677, true, 1613.891850, true], 6 | "10:50": [" 51:43", 0.212421, 0.102775, 0, 40440, 47.049677, true, 2018.627850, true], 7 | "10:60": [" 61:43", 0.212421, 0.102775, 360, 48600, 47.049677, true, 2423.363850, false], 8 | "10:70": [" 71:43", 0.212421, 0.102775, 720, 55680, 47.049677, true, 2828.099850, false], 9 | "20:10": [" 12:43", 3.212711, 1.667024, 0, 15060, 43.103360, true, 641.137617, true], 10 | "20:20": [" 23:26", 6.668992, 3.464350, 180, 31620, 64.113430, true, 1247.959617, true], 11 | "20:30": [" 34:28", 10.496570, 5.446659, 1020, 48120, 96.539699, true, 1854.781617, true], 12 | "20:40": [" 46:42", 16.253215, 8.140860, 1740, 60900, 146.861768, true, 2461.603617, false], 13 | "20:50": [" 60:28", 24.164326, 11.659135, 2400, 70500, 238.368373, true, 3068.425617, false], 14 | "30:10": [" 15:09", 6.793639, 2.631404, 120, 21780, 89.453192, true, 888.749367, true], 15 | "30:20": [" 28:39", 17.632512, 7.131404, 900, 45360, 158.439009, true, 1727.326822, true], 16 | "30:30": [" 45:05", 32.085703, 14.219367, 1920, 63960, 297.061117, true, 2582.216009, false], 17 | "30:40": [" 64:51", 48.594790, 22.152238, 3000, 78000, 477.336210, true, 3490.133500, false], 18 | "30:50": [" 86:04", 65.695588, 30.457793, 4260, 89520, 663.655187, true, 4460.528201, false], 19 | "40:10": [" 17:33", 10.242556, 3.958950, 240, 27420, 105.518107, true, 1176.134872, true], 20 | "40:20": [" 37:25", 29.532172, 12.994237, 1680, 58200, 266.798554, true, 2344.101723, false], 21 | "40:30": [" 63:02", 54.515149, 25.620681, 3060, 78720, 534.266750, true, 3581.303548, false], 22 | "40:40": [" 91:26", 80.916287, 38.922459, 5100, 94560, 815.260546, true, 4923.612197, false], 23 | "40:50": ["120:22", 107.516121, 53.245102, 7920, 105780, 1106.322245, true, 6276.579724, false], 24 | "50:10": [" 21:36", 14.561924, 6.227787, 300, 31860, 147.021556, true, 1516.283209, true], 25 | "50:20": [" 49:03", 42.969754, 20.102434, 2340, 68880, 410.384639, true, 3061.198605, false], 26 | "50:30": [" 84:36", 78.516093, 37.977143, 4680, 91800, 790.796192, true, 4778.243974, false], 27 | "50:40": ["124:12", 116.938661, 58.799706, 8940, 108480, 1201.168838, true, 6643.069245, false], 28 | "50:50": ["166:14", 154.131598, 77.675884, 15180, 120480, 1600.975041, false, 8609.432979, false], 29 | "60:10": [" 25:06", 17.703425, 8.654978, 480, 37140, 171.628255, true, 1872.093595, true], 30 | "60:20": [" 62:09", 57.005878, 29.990886, 3060, 78480, 563.062050, true, 3873.251088, false], 31 | "60:25": [" 85:33", 81.055882, 42.775364, 4800, 92700, 818.759658, true, 4995.074801, false], 32 | "60:30": ["109:31", 104.776035, 56.416868, 7080, 103200, 1075.232641, true, 6152.560334, false], 33 | "multilevel1": [" 78:42", 68.185237, 32.018200, 4020, 88080, 675.843222, true, 4440.868139, true] 34 | } 35 | -------------------------------------------------------------------------------- /dipplanner/tests/model_buhlmann_gradient_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test for buhlmann model gradient class.""" 23 | import unittest 24 | # import here the module / classes to be tested 25 | from dipplanner.main import activate_debug_for_tests 26 | 27 | from dipplanner.model.buhlmann.gradient import Gradient 28 | from dipplanner import settings 29 | 30 | 31 | class TestModelBuhlmannGradient(unittest.TestCase): 32 | """Base class for Buhlmann gradint tests.""" 33 | 34 | def setUp(self): 35 | """Init of the tests.""" 36 | super().setUp() 37 | 38 | # temporary hack (tests): 39 | activate_debug_for_tests() 40 | settings.RUN_TIME = True 41 | settings.SURFACE_TEMP = 12 42 | self.gradient1 = Gradient(0.3, 0.8) 43 | self.gradient2 = Gradient(0.35, 0.75) 44 | 45 | 46 | class TestModelBuhlmannGradientSimple1(TestModelBuhlmannGradient): 47 | """Test Gradients.""" 48 | 49 | def test_1(self): 50 | """test gradients 1.""" 51 | self.assertEqual(self.gradient1.gf_low, 0.3, 52 | "wrong gw_low : %s" % self.gradient1.gf_low) 53 | 54 | def test_2(self): 55 | """test gradients 2.""" 56 | self.assertEqual(self.gradient1.gf_high, 0.8, 57 | "wrong gw_high : %s" % self.gradient1.gf_high) 58 | 59 | def test_3(self): 60 | """test gradients 3.""" 61 | self.assertEqual(self.gradient1.gf_slope, 1.0, 62 | "wrong gw_slope : %s" % self.gradient1.gf_slope) 63 | 64 | def test_4(self): 65 | """test gradients 4.""" 66 | self.assertEqual(self.gradient1.gf, 0.3, 67 | "wrong gw_low : %s" % self.gradient1.gf) 68 | 69 | def test_5(self): 70 | """test gradients 5.""" 71 | self.gradient2.set_gf_slope_at_depth(12) 72 | self.assertEqual(round(self.gradient2.gf_slope, 13), -0.0333333333333, 73 | "wrong gw_slope : %s" % self.gradient2.gf_slope) 74 | 75 | def test_6(self): 76 | """test gradients 6.""" 77 | self.gradient2.set_gf_slope_at_depth(6) 78 | self.gradient2.set_gf_at_depth(6) 79 | self.assertEqual(self.gradient2.gf, 0.35, 80 | "wrong gw_low : %s" % self.gradient2.gf) 81 | -------------------------------------------------------------------------------- /dipplanner/tests/model_buhlmann_oxygen_toxicity_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test for oxygen toxicity class.""" 23 | import unittest 24 | # import here the module / classes to be tested 25 | from dipplanner.main import activate_debug_for_tests 26 | 27 | from dipplanner.model.buhlmann.oxygen_toxicity import OxTox 28 | from dipplanner import settings 29 | 30 | 31 | class TestModelBuhlmannOxTox(unittest.TestCase): 32 | """Test the Oxygen toxicity model.""" 33 | 34 | def setUp(self): 35 | """Init of the tests.""" 36 | super().setUp() 37 | # temporary hack (tests): 38 | activate_debug_for_tests() 39 | settings.RUN_TIME = True 40 | settings.SURFACE_TEMP = 12 41 | self.ox1 = OxTox() 42 | self.ox2 = OxTox() 43 | 44 | def test_cns(self): 45 | """test cns.""" 46 | self.assertEqual(self.ox1.cns, 0.0, 47 | "bad cns value : %s" % self.ox1.cns) 48 | def test_otu(self): 49 | """test otu.""" 50 | self.assertEqual(self.ox1.otu, 0.0, 51 | "bad otu value : %s" % self.ox1.otu) 52 | def test_maxox(self): 53 | """test maxox.""" 54 | self.assertEqual(self.ox1.max_ox, 0.0, 55 | "bad max_ox value : %s" % self.ox1.max_ox) 56 | def test_cns2(self): 57 | """test cns 2.""" 58 | self.ox1.add_o2(10 * 60, 1.3) 59 | self.assertEqual(round(self.ox1.cns, 13), 0.0555555555556, 60 | "bad cns value : %s" % self.ox1.cns) 61 | def test_otu2(self): 62 | """test otu 2.""" 63 | self.ox1.add_o2(10 * 60, 1.3) 64 | self.assertEqual(round(self.ox1.otu, 10), 14.7944872366, 65 | "bad otu value : %s" % self.ox1.otu) 66 | def test_cns3(self): 67 | """test cns 3.""" 68 | self.ox1.add_o2(10 * 60, 1.3) 69 | self.ox1.remove_o2(4 * 60 * 60) 70 | self.assertEqual(round(self.ox1.cns, 14), 0.00874945594818, 71 | "bad cns value : %s" % self.ox1.cns) 72 | def test_otu3(self): 73 | """test otu 3.""" 74 | self.ox1.add_o2(10 * 60, 1.3) 75 | self.ox1.remove_o2(4 * 60 * 60) 76 | self.assertEqual(round(self.ox1.otu, 10), 14.7944872366, 77 | "bad otu value : %s" % self.ox1.otu) 78 | def test_cns4(self): 79 | """test cns 4.""" 80 | self.ox1.add_o2(10 * 60, 1.3) 81 | self.ox1.remove_o2(25 * 60 * 60) 82 | self.assertEqual(round(self.ox1.cns, 7), 0.0000005, 83 | "bad cns value : %s" % round(self.ox1.cns, 7)) 84 | def test_otu4(self): 85 | """test otu 4.""" 86 | self.ox1.add_o2(10 * 60, 1.3) 87 | self.ox1.remove_o2(25 * 60 * 60) 88 | self.assertEqual(round(self.ox1.otu, 11), 0.0, 89 | "bad otu value : %s" % self.ox1.otu) 90 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Lesser General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """global setup file.""" 20 | 21 | from setuptools import setup, find_packages 22 | from setuptools.command.build_py import build_py 23 | from io import open 24 | import os 25 | 26 | # local imports 27 | from build_scripts.version import get_git_version 28 | 29 | __authors__ = [ 30 | # alphabetical order by last name 31 | 'Thomas Chiroux', ] 32 | 33 | 34 | with open("README.rst", encoding='utf-8') as f: 35 | README = f.read() 36 | 37 | with open("NEWS.rst", encoding='utf-8') as f: 38 | NEWS = f.read() 39 | 40 | 41 | VERSION = get_git_version() 42 | if VERSION is None: 43 | try: 44 | file_name = "dipplanner/RELEASE-VERSION" 45 | version_file = open(file_name, "r", encoding='utf-8') 46 | try: 47 | VERSION = version_file.readlines()[0] 48 | VERSION = VERSION.strip() 49 | except: 50 | VERSION = "0.0.0" 51 | finally: 52 | version_file.close() 53 | except IOError: 54 | VERSION = "0.0.0" 55 | 56 | 57 | class my_build_py(build_py): 58 | def run(self): 59 | # honor the --dry-run flag 60 | if not self.dry_run: 61 | target_dirs = [] 62 | target_dirs.append(os.path.join(self.build_lib, 'dipplanner')) 63 | target_dirs.append('dipplanner') 64 | 65 | # mkpath is a distutils helper to create directories 66 | for dir in target_dirs: 67 | self.mkpath(dir) 68 | 69 | try: 70 | for dir in target_dirs: 71 | fobj = open(os.path.join(dir, 'RELEASE-VERSION'), 'w', 72 | encoding='utf-8') 73 | fobj.write(VERSION) 74 | fobj.close() 75 | except: 76 | pass 77 | 78 | # distutils uses old-style classes, so no super() 79 | build_py.run(self) 80 | 81 | 82 | install_requires = [ 83 | # List your project dependencies here. 84 | # For more details, see: 85 | # http://packages.python.org/distribute/setuptools.html#declaring-dependencies 86 | 'jinja2', ] 87 | 88 | 89 | setup(name='dipplanner', 90 | version=VERSION, 91 | description="Dive planner and decompression calculation program", 92 | long_description=README + '\n\n' + NEWS, 93 | cmdclass={'build_py': my_build_py}, 94 | classifiers=[ 95 | # Get strings from 96 | # http://pypi.python.org/pypi?%3Aaction=list_classifiers 97 | "Programming Language :: Python :: 3", 98 | "Programming Language :: Python :: 3.4", 99 | "Programming Language :: Python :: 3.5"], 100 | keywords='diving plannification', 101 | author='Thomas Chiroux', 102 | author_email='', 103 | url='http://dipplanner.org', 104 | license='GPLv3', 105 | entry_points={ 106 | 'console_scripts': ['dipplanner = dipplanner.main:main', ], 107 | }, 108 | packages=find_packages(), 109 | package_data={'dipplanner': ['RELEASE-VERSION', 'templates/*', ]}, 110 | include_package_data=True, 111 | zip_safe=False, 112 | provides=('dipplanner', ), 113 | install_requires=install_requires, 114 | # test_suite = 'test.run_all_tests.run_all_tests', 115 | tests_require=['nose', 'coverage', ], 116 | test_suite='nose.collector', 117 | extras_require={ 118 | 'doc': ["sphinx", ], 119 | 'devel_tools': ["ipython", "pylint", "pep8", ], 120 | },) 121 | -------------------------------------------------------------------------------- /dipplanner/model/buhlmann/oxygen_toxicity.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2012 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Oxygen Toxicity model.""" 20 | 21 | import math 22 | import logging 23 | 24 | 25 | class OxTox(): 26 | """Define a Oxygen Toxicity model. 27 | 28 | *Attributes:* 29 | * cns (float) -- central nervous system toxicity 30 | 31 | (see http://en.wikipedia.org/wiki/Oxygen_toxicity#Signs_and_symptoms) 32 | * otu (float- -- Oxygen toxicity Units 33 | * max_ox (float) -- maximum ppo2 34 | """ 35 | 36 | def __init__(self): 37 | """Init of OxTox class.""" 38 | # initiate class logger 39 | self.logger = logging.getLogger( 40 | "dipplanner.model.buhlmann.oxygen_toxicity.OxTox") 41 | self.logger.debug("creating an instance of Oxtox") 42 | 43 | self.cns = 0.0 44 | self.otu = 0.0 45 | self.max_ox = 0.0 46 | 47 | def __deepcopy__(self, memo): 48 | """Deepcopy method will be called by copy.deepcopy. 49 | 50 | Used for "cloning" the object into another new object. 51 | 52 | :param memo: not used here 53 | 54 | :returns: Compartment object copy of itself 55 | :rtype: :class:`Model` 56 | """ 57 | newobj = OxTox() 58 | newobj.cns = self.cns 59 | newobj.otu = self.otu 60 | newobj.max_ox = self.max_ox 61 | return newobj 62 | 63 | def add_o2(self, time, pp_o2): 64 | """Add oxygen load into model. 65 | 66 | Uses NOAA lookup table to add percentage based on time and ppO2. 67 | Calculate OTU using formula OTU= T * (0.5/(pO2-0.5))^-(5/6) 68 | 69 | this OTU formula need T (time) in minutes, so we need to convert the 70 | time in second to minutes while using this formula 71 | 72 | :param float pp_o2: partial pressure of oxygen 73 | :param float time: time of segment (in seconds) 74 | """ 75 | if pp_o2 > 0.5: 76 | # only accumulate OTU for ppO2 > 0.5 atm 77 | diff_otu = ((float(time) / 60) * 78 | math.pow((0.5 / (pp_o2 - 0.5)), -0.833333)) 79 | self.otu += diff_otu 80 | 81 | # CNS calculation 82 | if pp_o2 > 1.8: 83 | exposure = 1 84 | elif pp_o2 > 1.7: 85 | exposure = 4 86 | elif pp_o2 > 1.6: 87 | exposure = 12 88 | elif pp_o2 > 1.5: 89 | exposure = 45 90 | elif pp_o2 > 1.4: 91 | exposure = 120 92 | elif pp_o2 > 1.3: 93 | exposure = 150 94 | elif pp_o2 > 1.2: 95 | exposure = 180 96 | elif pp_o2 > 1.1: 97 | exposure = 210 98 | elif pp_o2 > 1.0: 99 | exposure = 240 100 | elif pp_o2 > 0.9: 101 | exposure = 300 102 | elif pp_o2 > 0.8: 103 | exposure = 360 104 | elif pp_o2 > 0.7: 105 | exposure = 450 106 | elif pp_o2 > 0.6: 107 | exposure = 570 108 | elif pp_o2 > 0.5: 109 | exposure = 720 110 | else: 111 | exposure = 0 112 | 113 | if exposure > 0: 114 | self.cns += (float(time) / 60) / exposure 115 | 116 | if pp_o2 > self.max_ox: 117 | self.max_ox = pp_o2 118 | 119 | def remove_o2(self, time): 120 | """Remove oxygen load from model during surface intervals. 121 | 122 | :param float time: time of segment (in seconds) 123 | """ 124 | # very simple OTU recovery model: one day of no diving reset OTU 125 | if time >= 86400: 126 | self.otu = 0.0 127 | 128 | # decay cns with haltime of 90mins 129 | self.cns = self.cns * math.exp(-(float(time) / 60) * 0.693147 / 90.0) 130 | -------------------------------------------------------------------------------- /dipplanner/model/buhlmann/gradient.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Gradient module.""" 20 | 21 | import logging 22 | 23 | 24 | class Gradient(): 25 | """Define a Gradient Factor object. 26 | 27 | A GF Object maintains a low and high setting and is able to determine 28 | a GF for any depth between its initialisation depth (see setGfAtDepth()) 29 | and the surface. 30 | 31 | *Attributes (self.):* 32 | * gf_low (float) -- low Gradient factor, from 0.0 to 1.0 33 | * gf_high (float) -- high Gradient factor, from 0.0 to 1.0 34 | * gf (float) -- current gf 35 | * gf_slope (float) -- slope of the linear equation 36 | * gf_set (bool) -- Indicates that gf Slope has been initialised 37 | """ 38 | 39 | def __init__(self, gf_low, gf_high): 40 | """Init of Gradient object. 41 | 42 | :param float gf_low: low Gradient factor, from 0.0 to 1.0 43 | :param float gf_high: high Gradient factor, from 0.0 to 1.0 44 | 45 | :raises ValueError: if either gf_low of gf_high has wrong value 46 | """ 47 | # initiate class logger 48 | self.logger = logging.getLogger( 49 | "dipplanner.model.buhlmann.gradient.Gradient") 50 | self.logger.debug("creating an instance of Gradient") 51 | 52 | self.gf_low = None 53 | self.gf_high = None 54 | self.set_gf_low(gf_low) 55 | self.set_gf_high(gf_high) 56 | 57 | self.gf_slope = 1.0 58 | self.gf = gf_low 59 | self.gf_set = False 60 | 61 | def __deepcopy__(self, memo): 62 | """Deepcopy method will be called by copy.deepcopy. 63 | 64 | Used for "cloning" the object into another new object. 65 | 66 | :param memo: not used here 67 | 68 | :returns: Gradient object copy of itself 69 | :rtype: :class:`Gradient` 70 | """ 71 | newobj = Gradient(self.gf_low, self.gf_high) 72 | newobj.gf = self.gf 73 | newobj.gf_slope = self.gf_slope 74 | newobj.gf_set = self.gf_set 75 | return newobj 76 | 77 | def get_gradient_factor(self): 78 | """Return current GF with bounds checking. 79 | 80 | if gf < gf_low, returns gf_low 81 | 82 | :returns: gf 83 | :rtype: float 84 | """ 85 | if self.gf >= self.gf_low: 86 | return self.gf 87 | else: 88 | return self.gf_low 89 | 90 | def set_gf_at_depth(self, depth): 91 | """Set the gf for a given depth. 92 | 93 | :param float depth: current depth, in meter 94 | """ 95 | if (self.gf_slope < 1.0) and (depth >= 0.0): 96 | self.gf = (depth * self.gf_slope) + self.gf_high 97 | 98 | def set_gf_slope_at_depth(self, depth): 99 | """Set gf Slope at specified depth. 100 | 101 | Typically called once to initialise the GF slope. 102 | 103 | :param float depth: current depth, in meter 104 | """ 105 | if depth > 0: 106 | self.gf_slope = (self.gf_high - self.gf_low) / (0.0 - depth) 107 | self.gf_set = True 108 | 109 | def set_gf_low(self, value): 110 | """Set gf low setting. 111 | 112 | :param float value: low Gf, between 0.0 and 1.0 113 | 114 | :raises ValueError: if either gf_low of gf_high has wrong value 115 | """ 116 | if value < 0.0 or value > 1.0: 117 | raise ValueError("gf_low should be between 0.0 and 1.0") 118 | else: 119 | self.gf_low = float(value) 120 | 121 | def set_gf_high(self, value): 122 | """Set gf high setting. 123 | 124 | :param float value: high Gf, between 0.0 and 1.0 125 | 126 | :raises ValueError: if either gf_low of gf_high has wrong value 127 | """ 128 | if value < 0.0 or value > 1.0: 129 | raise ValueError("gf_high should be between 0.0 and 1.0") 130 | else: 131 | self.gf_high = float(value) 132 | -------------------------------------------------------------------------------- /dipplanner/settings.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Global settings for dipplannerand their default values. 20 | 21 | All the settings can be changed by :ref:`dipplanner_cmdline` 22 | and/or :ref:`dipplanner_configfile` 23 | """ 24 | 25 | # ======= "Internal" Settings ======== 26 | # software version, populated automatically 27 | __VERSION__ = None 28 | 29 | #: unless knowing what you're doing, this prefs should not be changed 30 | #: by the user 31 | 32 | FRESH_WATER_DENSITY = 1.0 #: water density kg/l 33 | SEA_WATER_DENSITY = 1.03 #: water density kg/l 34 | ABSOLUTE_MAX_PPO2 = 2.0 #: in bar 35 | ABSOLUTE_MIN_PPO2 = 0.16 #: in bar 36 | ABSOLUTE_MAX_TANK_PRESSURE = 300 #: in bar 37 | ABSOLUTE_MAX_TANK_SIZE = 2 * 20 #: in liter 38 | 39 | #: Temperature at surface. 40 | #: Used to calculate PP_H2O_SURFACE 41 | SURFACE_TEMP = 37 42 | 43 | HE_NARCOTIC_VALUE = 0.23 #: helium narcotic value 44 | N2_NARCOTIC_VALUE = 1.0 #: nitrogen narcotic value 45 | O2_NARCOTIC_VALUE = 1.0 #: oxygen narcotic value 46 | AR_NARCOTIC_VALUE = 2.33 #: argon narcotic value 47 | 48 | DEFAULT_AIR_FH2 = 0.0 #: fraction of HE in standard AIR 49 | DEFAULT_AIR_FN2 = 0.7808 #: fraction of N2 in standard AIR 50 | DEFAULT_AIR_FO2 = 0.2095 #: fraction of O2 in standard AIR 51 | DEFAULT_AIR_FAR = 0.00934 #: fraction of AR (argon) in standard AIR 52 | #: fraction of innert gas in standard AIR 53 | DEFAULT_AIR_F_INNERT_GAS = DEFAULT_AIR_FH2 + DEFAULT_AIR_FN2 + DEFAULT_AIR_FAR 54 | 55 | STOP_DEPTH_INCREMENT = 3 #: in meter 56 | LAST_STOP_DEPTH = 3 #: in meter : last stop before surfacing 57 | STOP_TIME_INCREMENT = 1 #: in second 58 | 59 | #: once deco stop begun, force to stop to each deco 60 | #: depth stop 61 | FORCE_ALL_STOPS = True 62 | 63 | AMBIANT_PRESSURE_SEA_LEVEL = 1.01325 #: surface pressure (in bar) 64 | METHOD_FOR_DEPTH_CALCULATION = 'complex' #: either simple (/10) or complex 65 | 66 | TRAVEL_SWITCH = 'late' #: "late" or "early" 67 | 68 | #: in meter practicly, this value can not be bigger than 69 | #: about 2850m because breathing air at 0m will only 70 | #: 'prepare the body' to a decompression until this 71 | #: value. To go higher, another 'stop' is needed 72 | #: between. 73 | FLIGHT_ALTITUDE = 2450 74 | 75 | # ========= User settings ======== 76 | TEMPLATE = "default-color.tpl" #: template should be in templates/ directory 77 | 78 | DECO_MODEL = "ZHL16c" #: ZHL16c or ZHL16b or ZHL16a 79 | BUHLMANN_VALUES = "1a" #: 1a (4 mins for 1st comp) or 1b (5 mins) 80 | 81 | 82 | WATER_DENSITY = SEA_WATER_DENSITY #: water density kg/l 83 | 84 | #: surface pressure (in bar) 85 | AMBIANT_PRESSURE_SURFACE = AMBIANT_PRESSURE_SEA_LEVEL 86 | 87 | DEFAULT_MAX_PPO2 = 1.6 #: in bar 88 | DEFAULT_MIN_PPO2 = 0.16 #: in bar 89 | DEFAULT_MAX_END = 30 #: in meter 90 | 91 | DIVE_CONSUMPTION_RATE = 20.0 / 60 #: liter/s 92 | DECO_CONSUMPTION_RATE = 17.0 / 60 #: liter/s 93 | 94 | DESCENT_RATE = 20 / 60 #: m/s 95 | ASCENT_RATE = 10 / 60 #: m/s 96 | DECO_ASCENT_RATE = 3 / 60 #: m/s 97 | 98 | #: Warning : if RUN-TIME is True, the segment duration must 99 | #: include descent time 100 | #: if the duration is too small dipplanner will raise an error 101 | #: if True: segments represents runtime, 102 | #: if false, segments represents segtime 103 | RUN_TIME = True 104 | 105 | USE_OC_DECO = True #: if True, use enabled gases of decomp in oc or bailout 106 | 107 | GF_LOW = 0.30 #: % between 0.0 and 1.0 108 | GF_HIGH = 0.80 #: % between 0.0 and 1.0 109 | 110 | #: On dives with multiple depth segments it is possible that 111 | #: decompression encountered during the excursion from a deep to 112 | #: shallower segment will initialise the gradient factors prematurely. 113 | #: Selecting this option keeps the gradient factor set at low until the last 114 | #: dive segment is executed. 115 | #: (normal behaviour is to initialise the gradient factor to Low at 116 | #: the first deco stop encountered. From then on it linearly increases the 117 | #: gradient factor until the final stop when it is set to High) 118 | MULTILEVEL_MODE = False 119 | 120 | AUTOMATIC_TANK_REFILL = True #: automatic refill of tanks between dives 121 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_txhypo_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "multilevel1": [" 98:53", 50.979069, 31.066469, 6660, 47880, 972.450264, true, 1110.462159, true, 3735.653850, true], 3 | "100:10": [" 49:07", 24.752898, 12.827637, 1680, 30540, 387.728697, true, 1639.438839, true, 2218.617945, true], 4 | "100:15": [" 93:58", 64.102125, 34.093098, 5760, 44220, 919.298733, true, 2796.437046, true, 3330.372945, true], 5 | "100:20": ["146:42", 110.255895, 58.971509, 11580, 57240, 1548.028841, false, 4154.392279, false, 4442.127945, false], 6 | "100:5": [" 16:26", 1.462652, 0.676202, 0, 3600, 64.113430, true, 686.334072, true, 1106.862945, true], 7 | "110:10": [" 52:05", 26.218295, 13.507228, 1800, 31080, 404.107730, true, 1751.725438, true, 2547.064095, true], 8 | "110:15": ["103:06", 71.610168, 38.257527, 6660, 45420, 1010.651008, true, 3171.139825, true, 3759.862095, true], 9 | "110:20": ["165:10", 126.148603, 68.853803, 13440, 61920, 1746.292575, false, 4833.023919, false, 4972.660095, false], 10 | "110:5": [" 18:09", 1.131041, 0.558156, 0, 4380, 89.453192, true, 686.334072, true, 1334.266095, true], 11 | "120:10": [" 54:22", 27.019804, 14.036962, 1800, 31260, 412.138989, true, 1851.091183, true, 2885.614545, true], 12 | "120:15": ["112:01", 78.917200, 42.897153, 7260, 46680, 1103.671747, true, 3545.736655, true, 4199.455545, true], 13 | "120:20": ["185:57", 143.296562, 79.059826, 15240, 66120, 1974.892774, false, 5305.964162, false, 5828.927705, false], 14 | "120:5": [" 19:52", 0.520327, 0.260950, 60, 5340, 89.453192, true, 716.003527, true, 1571.773545, true], 15 | "130:10": [" 55:57", 27.062248, 14.411669, 1800, 31080, 410.475320, true, 1937.307841, true, 3234.269295, true], 16 | "130:15": ["120:55", 85.674341, 47.126340, 7980, 49800, 1186.830578, true, 3762.605799, false, 4770.136820, false], 17 | "130:20": ["205:15", 159.547695, 89.295637, 16740, 69780, 2186.573986, false, 5753.384777, false, 6708.378119, false], 18 | "130:5": [" 21:35", -0.217259, -0.234182, 60, 6360, 89.453192, true, 750.002674, true, 1819.385295, true], 19 | "140:10": [" 56:17", 26.404473, 18.114804, 1740, 30540, 399.747356, true, 1928.221309, true, 3593.028345, true], 20 | "140:15": ["128:28", 90.716622, 61.315518, 8580, 52080, 1248.555718, true, 3919.767675, false, 5457.745262, false], 21 | "140:20": ["224:31", 176.651115, 115.727891, 17880, 72480, 2417.814430, false, 6176.667142, false, 7609.379493, false], 22 | "140:5": [" 23:32", -0.809089, -3.147149, 120, 7440, 94.760715, true, 788.331514, true, 2077.101345, true], 23 | "150:10": [" 55:47", 24.971950, 32.217862, 1620, 29640, 378.777320, true, 1898.360485, true, 3961.891695, true], 24 | "150:15": ["135:46", 96.130057, 110.327248, 9240, 54300, 1316.758968, true, 4076.873093, false, 6083.103044, false], 25 | "150:20": ["243:19", 192.028347, 201.091274, 18960, 75300, 2618.046364, false, 6631.252961, false, 8533.507213, false], 26 | "150:5": [" 25:42", -1.189726, -18.791258, 120, 8580, 106.119313, true, 830.990046, true, 2344.921695, true], 27 | "50:10": [" 26:45", 9.199949, 5.474013, 780, 22200, 210.005566, true, 711.617534, true, 947.911503, true], 28 | "50:20": [" 63:05", 34.268038, 20.127750, 3300, 39660, 614.547515, true, 1229.525199, true, 2160.991503, true], 29 | "50:30": ["104:46", 63.298752, 36.785555, 7260, 48120, 1077.892064, true, 1885.535760, true, 3374.071503, true], 30 | "50:40": ["150:51", 95.689106, 55.184580, 12300, 53220, 1586.268601, false, 2656.422860, true, 4587.151503, false], 31 | "50:50": ["197:37", 128.736007, 74.369000, 16080, 57300, 2116.069241, false, 3436.466469, true, 5800.231503, false], 32 | "60:10": [" 31:37", 13.178930, 7.027351, 1080, 24840, 253.089195, true, 959.460413, true, 1094.423853, true], 33 | "60:20": [" 78:09", 48.126539, 26.374482, 4620, 42960, 779.047178, true, 1794.104605, true, 2509.589853, true], 34 | "60:30": ["132:58", 90.250799, 50.177588, 10320, 51120, 1409.777310, true, 2815.726133, true, 3924.755853, true], 35 | "60:40": ["191:35", 134.529524, 74.303592, 15960, 60420, 2072.040861, false, 3958.823951, false, 5339.921853, false], 36 | "70:10": [" 36:47", 16.894699, 9.034225, 1260, 26820, 297.061117, true, 1209.600650, true, 1293.905295, true], 37 | "70:20": [" 94:03", 62.936295, 34.277500, 6300, 45660, 957.759534, true, 2377.511244, true, 2911.157295, true], 38 | "70:30": ["164:22", 119.301551, 64.096025, 13680, 57720, 1773.568949, false, 3842.697970, false, 4528.409295, false], 39 | "80:10": [" 41:03", 20.198078, 10.769892, 1440, 28500, 334.977171, true, 1333.680960, true, 1592.038545, true], 40 | "80:20": ["111:42", 78.662605, 41.712395, 7800, 47820, 1156.513954, true, 2934.317662, true, 3411.376545, true], 41 | "80:30": ["195:26", 147.915367, 79.010118, 16320, 65340, 2130.784605, false, 4871.031857, false, 5230.714545, false], 42 | "90:10": [" 45:25", 22.572698, 11.950223, 1560, 29640, 362.054740, true, 1514.317270, true, 1900.276095, true], 43 | "90:20": ["129:20", 94.336189, 50.636040, 9540, 51900, 1349.686411, true, 3541.049648, true, 3921.700095, true], 44 | "90:30": ["230:27", 181.259601, 97.054339, 18540, 71820, 2553.050344, false, 6001.982928, false, 5943.124095, false] 45 | } 46 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_txhypo_forcedtravel_test.json: -------------------------------------------------------------------------------- 1 | { 2 | "multilevel1": ["107:09", 55.439676, 33.636431, 7320, 49020, 1058.477573, true, 1222.203812, true, 3988.402350, true], 3 | "100:10": [" 96:49", 66.218412, 35.354302, 6120, 44760, 948.295234, true, 2856.102263, true, 3398.422992, true], 4 | "100:15": ["149:45", 112.583522, 60.357013, 11940, 57960, 1580.784511, false, 4216.921180, false, 4510.177992, false], 5 | "100:20": ["207:53", 162.362637, 86.883953, 17040, 69120, 2264.820505, false, 5745.594831, false, 5621.932992, false], 6 | "100:5": [" 51:37", 26.473510, 13.599886, 1800, 31620, 413.343798, true, 1679.966401, true, 2286.667992, true], 7 | "110:10": ["112:12", 79.772365, 42.706815, 7440, 46740, 1124.754701, true, 3367.560805, true, 3949.191942, true], 8 | "110:15": ["175:20", 133.804537, 72.526397, 14280, 63600, 1857.839511, false, 5026.779355, false, 5273.923555, false], 9 | "110:20": ["244:47", 195.035350, 107.105255, 19380, 75180, 2686.053208, false, 6716.184275, false, 6669.112047, false], 10 | "110:5": [" 59:11", 32.016250, 16.817236, 2280, 34020, 483.468377, true, 1951.622688, true, 2736.393942, true], 11 | "120:10": ["128:49", 92.929746, 51.053758, 9060, 52800, 1286.785890, true, 3979.248470, false, 4530.273792, false], 12 | "120:15": ["203:52", 158.385750, 87.527404, 16680, 69300, 2179.001587, false, 5716.753323, false, 6282.687315, false], 13 | "120:20": ["288:56", 234.023524, 129.955955, 21060, 79620, 3216.867044, false, 7766.911069, false, 7879.363219, false], 14 | "120:5": [" 67:25", 38.309301, 20.391519, 2760, 36180, 566.477672, true, 2247.699298, true, 3216.432792, true], 15 | "130:10": ["148:18", 109.527417, 62.284433, 11100, 57780, 1510.282163, false, 4395.438870, false, 5417.463389, false], 16 | "130:15": ["235:50", 187.015056, 104.860263, 18660, 74100, 2563.778308, false, 6467.605146, false, 7367.604270, false], 17 | "130:20": ["333:47", 271.600711, 152.890687, 22560, 83760, 3716.498199, false, 8865.193051, false, 9288.816188, false], 18 | "130:5": [" 76:07", 45.187111, 24.744392, 3360, 38340, 653.756661, true, 2518.251430, true, 3726.784542, true], 19 | "140:10": ["169:54", 127.092495, 84.346819, 13380, 62640, 1746.636121, false, 4867.113051, false, 6388.253677, false], 20 | "140:15": ["272:25", 217.566714, 142.760719, 20340, 78180, 2979.516669, false, 7335.571078, false, 8546.688360, false], 21 | "140:20": ["384:35", 315.635061, 205.290633, 23580, 87060, 4308.713469, false, 10129.083218, false, 10683.283758, false], 22 | "140:5": [" 84:45", 51.711178, 35.418800, 4080, 40260, 733.374967, true, 2876.267362, true, 4267.449192, true], 23 | "150:10": ["193:12", 146.048350, 155.595321, 15420, 67140, 2002.026802, false, 5393.809755, false, 7409.394577, false], 24 | "150:15": ["309:46", 250.673472, 251.723669, 21780, 81960, 3425.012525, false, 8209.301661, false, 9825.122206, false], 25 | "150:20": ["445:53", 368.578307, 355.201766, 23940, 88740, 5048.744838, false, 11550.225279, false, 12275.759431, false], 26 | "150:5": [" 95:30", 59.227359, 69.467047, 4860, 42240, 833.371692, true, 3077.706354, true, 5066.623661, false], 27 | "50:10": [" 36:59", 15.945191, 9.518888, 1380, 29220, 319.317055, true, 851.761752, true, 1319.231550, true], 28 | "50:20": [" 75:15", 42.533513, 24.717263, 4440, 42840, 742.905313, true, 1415.293186, true, 2532.311550, true], 29 | "50:30": ["118:22", 72.348370, 42.318976, 8640, 49980, 1217.458924, true, 2120.083583, true, 3745.391550, true], 30 | "50:40": ["166:27", 106.566470, 61.379381, 13620, 54300, 1759.226559, false, 2921.403286, true, 4958.471550, false], 31 | "50:50": ["212:14", 139.999321, 80.847353, 16920, 59220, 2295.075876, false, 3659.994446, false, 6171.551550, false], 32 | "60:10": [" 46:55", 24.440674, 13.202164, 1980, 33300, 422.950443, true, 1214.889900, true, 1587.023700, true], 33 | "60:20": [" 96:30", 62.508407, 34.852503, 6600, 46440, 991.778868, true, 2118.618128, true, 3002.189700, true], 34 | "60:30": ["153:30", 105.621623, 58.032924, 12600, 53220, 1637.611721, false, 3206.547538, true, 4417.355700, false], 35 | "60:40": ["212:50", 152.044985, 83.924940, 17220, 63300, 2337.727675, false, 4332.554597, false, 5832.521700, false], 36 | "70:10": [" 57:33", 33.739635, 18.081267, 2640, 36540, 540.749654, true, 1591.827744, true, 1927.993542, true], 37 | "70:20": ["120:01", 82.876027, 45.137152, 8760, 49320, 1248.752125, true, 2927.076608, true, 3545.245542, true], 38 | "70:30": ["191:23", 140.301457, 76.008587, 16020, 62940, 2076.715353, false, 4436.794459, false, 5162.497542, false], 39 | "80:10": [" 69:37", 43.828617, 23.513254, 3600, 39600, 667.981339, true, 1956.510154, true, 2387.823792, true], 40 | "80:20": ["146:26", 107.562123, 57.936776, 11820, 55320, 1564.091359, false, 3719.327524, false, 4207.161792, true], 41 | "80:30": ["235:31", 181.619642, 97.417134, 18840, 71220, 2604.162835, false, 5772.828597, false, 6026.499792, false], 42 | "90:10": [" 82:40", 54.361340, 28.844473, 4680, 42300, 799.840647, true, 2376.931386, true, 2877.966942, true], 43 | "90:20": ["177:21", 134.646552, 71.794393, 14700, 62880, 1911.675737, false, 4689.768987, false, 4899.390942, false], 44 | "90:30": ["283:05", 225.461713, 120.218630, 20820, 77220, 3174.558791, false, 7279.767146, false, 6920.814942, false] 45 | } 46 | -------------------------------------------------------------------------------- /dipplanner/main.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Main dipplanner module for command line usage. 20 | 21 | This module is used by the only "executable" of the project: 22 | bin/dipplanner (which is an empty shell) 23 | 24 | runs in command line and output resulting dive profile 25 | also initiate log files 26 | """ 27 | import sys 28 | import logging 29 | 30 | # dependencies imports 31 | from jinja2 import Environment, PackageLoader 32 | 33 | # local imports 34 | from dipplanner.parse_cli_args import DipplannerCliArguments 35 | from dipplanner import settings 36 | from dipplanner.dive import Dive 37 | 38 | LOGGER = logging.getLogger("dipplanner") 39 | 40 | 41 | def activate_debug(): 42 | """Setup the default debug parameters. 43 | 44 | it's mainly used for test cases who needs also logging to be set 45 | """ 46 | LOGGER.setLevel(logging.DEBUG) 47 | # create file handler which logs even debug messages 48 | file_handler = logging.FileHandler("dipplanner.log") 49 | file_handler.setLevel(logging.DEBUG) 50 | # create console handler with a higher log level 51 | stream_handler = logging.StreamHandler() 52 | stream_handler.setLevel(logging.WARNING) 53 | # create formatter and add it to the handlers 54 | formatter = logging.Formatter( 55 | "%(asctime)s - %(levelname)s - %(name)s - %(message)s") 56 | file_handler.setFormatter(formatter) 57 | stream_handler.setFormatter(formatter) 58 | # add the handlers to the logger 59 | LOGGER.addHandler(file_handler) 60 | LOGGER.addHandler(stream_handler) 61 | 62 | 63 | def activate_debug_for_tests(): 64 | """Setup the default debug parameters. 65 | 66 | it's mainly used for test cases who needs also logging to be set 67 | """ 68 | LOGGER.setLevel(logging.CRITICAL) 69 | stream_handler = logging.StreamHandler() 70 | stream_handler.setLevel(logging.INFO) 71 | formatter = logging.Formatter( 72 | "%(asctime)s - %(levelname)s - %(name)s - %(message)s") 73 | stream_handler.setFormatter(formatter) 74 | LOGGER.addHandler(stream_handler) 75 | 76 | 77 | def main(cli_arguments=None): 78 | """Main entry point. 79 | 80 | main uses the parameters, tanks and dives given in config file(s) 81 | and/or command line, calculates the dives and return the output in stdout. 82 | 83 | :param list cli_arguments: list of arguments, like sys.argv 84 | """ 85 | if sys.version_info < (3, 4): 86 | raise SystemExit("ERROR: This programm needs python 3.4 or greater") 87 | 88 | if cli_arguments is None: 89 | cli_arguments = sys.argv 90 | 91 | activate_debug() 92 | 93 | # get the version 94 | try: 95 | version_file = open("RELEASE-VERSION", "r") 96 | try: 97 | version = version_file.readlines()[0] 98 | settings.__VERSION__ = version.strip() 99 | finally: 100 | version_file.close() 101 | except IOError: 102 | settings.__VERSION__ = "unknown" 103 | 104 | dipplanner_arguments = DipplannerCliArguments(cli_arguments) 105 | dives = dipplanner_arguments.dives 106 | 107 | profiles = [] 108 | current_dive = None 109 | previous_dive = None 110 | for dive in dives: 111 | if previous_dive is None: 112 | current_dive = Dive( 113 | dives[dive]['segments'].values(), 114 | dives[dive]['tanks'].values()) 115 | else: 116 | current_dive = Dive( 117 | dives[dive]['segments'].values(), 118 | dives[dive]['tanks'].values(), 119 | previous_dive 120 | ) 121 | if dives[dive]['surface_interval']: 122 | current_dive.do_surface_interval(dives[dive]['surface_interval']) 123 | 124 | current_dive.do_dive_without_exceptions() 125 | profiles.append(current_dive) 126 | previous_dive = current_dive 127 | # now, dive exceptins do not stop the program anymore, but can be 128 | # displayed in the output template instead. The used MUST take care of 129 | # the result. 130 | 131 | # now calculate no flight time based on the last dive 132 | ######current_dive.no_flight_time_wo_exception() 133 | 134 | # now Prepare the output 135 | env = Environment(loader=PackageLoader('dipplanner', 'templates')) 136 | tpl = env.get_template(settings.TEMPLATE) 137 | # pylint: disable=no-member 138 | text = tpl.render(settings=settings, dives=profiles) 139 | # pylint: enable=no-member 140 | print(text) 141 | -------------------------------------------------------------------------------- /configs/default_config.cfg: -------------------------------------------------------------------------------- 1 | # dipplanner config file 2 | # this file is used by the command line tool and 3 | # override the defaults parameters or input some dive profiles 4 | 5 | # This file represent default configuration, without any dive profile nor tank. 6 | 7 | # =============================== dive profiles ================================ 8 | # repetitive dives are given using [diveXXX] section, where XXX represent a 9 | # number. 10 | # the dives will be done in croissant order. 11 | #[dive1] 12 | 13 | # Tank list for this dive: 14 | # Format: tankXXX=tank_name;fO2;fHe;Volume(l);Pressure(bar) 15 | #tank1 = airtank;0.21;0.0;15;230;50b 16 | 17 | # segment list for this dive. At least ONE segment is mandatory 18 | # Format: segmentXXX=depth(m);duration(s);tank_name;set_point(for ccr) 19 | #segment1 = 30;20*60;airtank;0.0 20 | 21 | #[dive2] 22 | # surface_interval (in seconds) 23 | # for repetitive dives, you can specify the surface time between the previous 24 | # dive and this dive 25 | #surface_interval = 60*60 26 | 27 | # Tanks 28 | # see dive 1 for more explanation 29 | # tank list is not mandatory for repetitive dives : if not given 30 | # last dive tanks will be used. 31 | # if 'automatic_tank_refill' is set to True, the tank will be full before the 32 | # dive. If set to False, it'll use the remaining gas from last dive 33 | # If at least ONE tank is provided for a repetitive dive, ALL the Tank MUST 34 | # be specified 35 | # newtank = txtank;0.21;0.30;15;230;50b 36 | # tank1 = airtank;0.21;0.0;15;230;50b 37 | 38 | # segment list for this dive. At least ONE segment is mandatory 39 | #segment1 = 20;30*60;airtank;0.0 40 | 41 | #[dive3]... 42 | 43 | # ============================== Other parameters ============================== 44 | [output] 45 | # template used for output result 46 | # templating uses jinja2, see documentation for more infos 47 | template = default-color.tpl 48 | 49 | [general] 50 | # deco model 51 | # choose between buhlmann ZHL16b or ZHL16c 52 | # ZHL16c is the default 53 | deco_model = ZHL16c 54 | 55 | # ppo2 56 | # defines the max and min_ppo2 57 | # default values : 58 | # max_ppo2 : 1.6 59 | # min_ppo2 : 0.21 60 | max_ppo2 = 1.6 61 | min_ppo2 = 0.21 62 | 63 | # max end 64 | # defines the max END for the dives 65 | # default value (in meter): 66 | # max_end : 30 67 | max_end = 30 68 | 69 | # decent and ascent rate, in m/minute 70 | descent_rate = 20 71 | ascent_rate = 10 72 | 73 | # Gradient factors in % 74 | gf_low = 30 75 | gf_high = 80 76 | 77 | # type of water 78 | # possible values : 79 | # sea -- sea water 80 | # fresh -- fresh water 81 | water = sea 82 | 83 | # dive altitude 84 | # in meter 85 | altitude = 0 86 | 87 | # consumption rates 88 | # in liter / minute (the program does the conversion internally) 89 | dive_consumption_rate = 17 90 | deco_consumption_rate = 12 91 | 92 | # run_time flag 93 | # if true: segments represents runtime, 94 | # if false, segments represents segtime 95 | run_time = true 96 | 97 | # Use Open Circuit deco flag 98 | # if True, use enabled gases of decomp in oc or bailout 99 | use_oc_deco = true 100 | 101 | # multilevel_mode 102 | multilevel_mode = false 103 | 104 | # automatic_tank_refill 105 | # if 'automatic_tank_refill' is set to True, the tank will be full before the 106 | # dive. If set to False, it'll use the remaining gas from last dive 107 | automatic_tank_refill = true 108 | 109 | # ========================== Advanced parameters ============================== 110 | # ========================= "Internal" Settings ================================ 111 | # !!! unless knowing what you're doing, this prefs should not be changed !!! 112 | # !!! by the user !!! 113 | # ============================================================================== 114 | [advanced] 115 | # water density kg/l 116 | fresh_water_density = 1.0 117 | sea_water_density = 1.03 118 | absolute_max_ppo2 = 2.0 119 | absolute_min_ppo2 = 0.16 120 | 121 | # in bar 122 | absolute_max_tank_pressure = 300 123 | 124 | # in liter 125 | absolute_max_tank_size = 40 126 | 127 | # temperature at surface 128 | surface_temp = 20 129 | 130 | he_narcotic_value = 0.23 131 | n2_narcotic_value = 1.0 132 | o2_narcotic_value = 1.0 133 | ar_narcotic_value = 2.33 134 | 135 | # in meter 136 | stop_depth_increment = 3 137 | 138 | # in meter : last stop before surfacing 139 | last_stop_depth = 3 140 | 141 | # in second 142 | stop_time_increment = 1 143 | 144 | # one deco stop begun, force to stop to each deco depth 145 | # stop 146 | force_all_stops = true 147 | 148 | # surface pressure at sea level (in bar) 149 | ambiant_pressure_sea_level = 1.01325 150 | 151 | # either simple (/10) or complex 152 | method_for_depth_calculation = complex 153 | 154 | # travel switch method 155 | # if 'late', dipplanner will try to keep the travel as long as possible 156 | # until either MOD or max END 157 | # if 'early', dipplanner will switch to bottom tank as soon as is it breathable 158 | travel_switch = late 159 | 160 | # flight altitude 161 | # parameter used in no flight time calculation : it's the parameter needed 162 | # to calculate decompression until the altitude of the flight 163 | # the default value represents the maximum 'altitude equivalent' tolerated 164 | # in flight by international regulation 165 | # (8000 feet = 2 438.4 meters rounded to 2450m) 166 | flight_altitude = 2450 -------------------------------------------------------------------------------- /dipplanner/tests/configs/restore_default_config.cfg: -------------------------------------------------------------------------------- 1 | # dipplanner config file 2 | # this file is used by the command line tool and 3 | # override the defaults parameters or input some dive profiles 4 | 5 | # This file represent default configuration, without any dive profile nor tank. 6 | 7 | # =============================== dive profiles ================================ 8 | # repetitive dives are given using [diveXXX] section, where XXX represent a 9 | # number. 10 | # the dives will be done in croissant order. 11 | #[dive1] 12 | 13 | # Tank list for this dive: 14 | # Format: tankXXX=tank_name;fO2;fHe;Volume(l);Pressure(bar) 15 | #tank1 = airtank;0.21;0.0;15;230;50b 16 | 17 | # segment list for this dive. At least ONE segment is mandatory 18 | # Format: segmentXXX=depth(m);duration(s);tank_name;set_point(for ccr) 19 | #segment1 = 30;20*60;airtank;0.0 20 | 21 | #[dive2] 22 | # surface_interval (in seconds) 23 | # for repetitive dives, you can specify the surface time between the previous 24 | # dive and this dive 25 | #surface_interval = 60*60 26 | 27 | # Tanks 28 | # see dive 1 for more explanation 29 | # tank list is not mandatory for repetitive dives : if not given 30 | # last dive tanks will be used. 31 | # if 'automatic_tank_refill' is set to True, the tank will be full before the 32 | # dive. If set to False, it'll use the remaining gas from last dive 33 | # If at least ONE tank is provided for a repetitive dive, ALL the Tank MUST 34 | # be specified 35 | # newtank = txtank;0.21;0.30;15;230;50b 36 | # tank1 = airtank;0.21;0.0;15;230;50b 37 | 38 | # segment list for this dive. At least ONE segment is mandatory 39 | #segment1 = 20;30*60;airtank;0.0 40 | 41 | #[dive3]... 42 | 43 | # ============================== Other parameters ============================== 44 | [output] 45 | # template used for output result 46 | # templating uses jinja2, see documentation for more infos 47 | template = default-color.tpl 48 | 49 | [general] 50 | # deco model 51 | # choose between buhlmann ZHL16b or ZHL16c 52 | # ZHL16c is the default 53 | deco_model = ZHL16c 54 | 55 | # ppo2 56 | # defines the max and min_ppo2 57 | # default values : 58 | # max_ppo2 : 1.6 59 | # min_ppo2 : 0.21 60 | max_ppo2 = 1.6 61 | min_ppo2 = 0.21 62 | 63 | # max end 64 | # defines the max END for the dives 65 | # default value (in meter): 66 | # max_end : 30 67 | max_end = 30 68 | 69 | # decent and ascent rate, in m/minute 70 | descent_rate = 20 71 | ascent_rate = 10 72 | 73 | # Gradient factors in % 74 | gf_low = 30 75 | gf_high = 80 76 | 77 | # type of water 78 | # possible values : 79 | # sea -- sea water 80 | # fresh -- fresh water 81 | water = sea 82 | 83 | # dive altitude 84 | # in meter 85 | altitude = 0 86 | 87 | # consumption rates 88 | # in liter / minute (the program does the conversion internally) 89 | dive_consumption_rate = 20 90 | deco_consumption_rate = 17 91 | 92 | # run_time flag 93 | # if true: segments represents runtime, 94 | # if false, segments represents segtime 95 | run_time = true 96 | 97 | # Use Open Circuit deco flag 98 | # if True, use enabled gases of decomp in oc or bailout 99 | use_oc_deco = true 100 | 101 | # multilevel_mode 102 | multilevel_mode = false 103 | 104 | # automatic_tank_refill 105 | # if 'automatic_tank_refill' is set to True, the tank will be full before the 106 | # dive. If set to False, it'll use the remaining gas from last dive 107 | automatic_tank_refill = true 108 | 109 | # ========================== Advanced parameters ============================== 110 | # ========================= "Internal" Settings ================================ 111 | # !!! unless knowing what you're doing, this prefs should not be changed !!! 112 | # !!! by the user !!! 113 | # ============================================================================== 114 | [advanced] 115 | # water density kg/l 116 | fresh_water_density = 1.0 117 | sea_water_density = 1.03 118 | absolute_max_ppo2 = 2.0 119 | absolute_min_ppo2 = 0.16 120 | 121 | # in bar 122 | absolute_max_tank_pressure = 300 123 | 124 | # in liter 125 | absolute_max_tank_size = 40 126 | 127 | # temperature at surface 128 | surface_temp = 20 129 | 130 | he_narcotic_value = 0.23 131 | n2_narcotic_value = 1.0 132 | o2_narcotic_value = 1.0 133 | ar_narcotic_value = 2.33 134 | 135 | # in meter 136 | stop_depth_increment = 3 137 | 138 | # in meter : last stop before surfacing 139 | last_stop_depth = 3 140 | 141 | # in second 142 | stop_time_increment = 1 143 | 144 | # one deco stop begun, force to stop to each deco depth 145 | # stop 146 | force_all_stops = true 147 | 148 | # surface pressure at sea level (in bar) 149 | ambiant_pressure_sea_level = 1.01325 150 | 151 | # either simple (/10) or complex 152 | method_for_depth_calculation = complex 153 | 154 | # travel switch method 155 | # if 'late', dipplanner will try to keep the travel as long as possible 156 | # until either MOD or max END 157 | # if 'early', dipplanner will switch to bottom tank as soon as is it breathable 158 | travel_switch = late 159 | 160 | # flight altitude 161 | # parameter used in no flight time calculation : it's the parameter needed 162 | # to calculate decompression until the altitude of the flight 163 | # the default value represents the maximum 'altitude equivalent' tolerated 164 | # in flight by international regulation 165 | # (8000 feet = 2 438.4 meters rounded to 2450m) 166 | flight_altitude = 2450 167 | -------------------------------------------------------------------------------- /dipplanner/tests/configs/test_config.cfg: -------------------------------------------------------------------------------- 1 | # dipplanner config file 2 | # this file is used by the command line tool and 3 | # override the defaults parameters or input some dive profiles 4 | 5 | # This file represent default configuration, without any dive profile nor tank. 6 | 7 | # =============================== dive profiles ================================ 8 | # repetitive dives are given using [diveXXX] section, where XXX represent a 9 | # number. 10 | # the dives will be done in croissant order. 11 | [dive1] 12 | 13 | # Tank list for this dive: 14 | # Format: tankXXX=tank_name;fO2;fHe;Volume(l);Pressure(bar) 15 | tank1 = airtank;0.21;0.0;15;230;50b 16 | 17 | # segment list for this dive. At least ONE segment is mandatory 18 | # Format: segmentXXX=depth(m);duration(s);tank_name;set_point(for ccr) 19 | segment1 = 30;20*60;airtank;0.0 20 | 21 | [dive2] 22 | # surface_interval (in seconds) 23 | # for repetitive dives, you can specify the surface time between the previous 24 | # dive and this dive 25 | surface_interval = 98*60 26 | 27 | # Tanks 28 | # see dive 1 for more explanation 29 | # tank list is not mandatory for repetitive dives : if not given 30 | # last dive tanks will be used. 31 | # if 'automatic_tank_refill' is set to True, the tank will be full before the 32 | # dive. If set to False, it'll use the remaining gas from last dive 33 | # If at least ONE tank is provided for a repetitive dive, ALL the Tank MUST 34 | # be specified 35 | # newtank = txtank;0.21;0.30;15;230;50b 36 | # tank1 = airtank;0.21;0.0;15;230;50b 37 | 38 | # segment list for this dive. At least ONE segment is mandatory 39 | segment1 = 20;30*60;airtank;0.0 40 | 41 | #[dive3]... 42 | 43 | # ============================== Other parameters ============================== 44 | [output] 45 | # template used for output result 46 | # templating uses jinja2, see documentation for more infos 47 | template = default.html 48 | 49 | [general] 50 | # deco model 51 | # choose between buhlmann ZHL16b or ZHL16c 52 | # ZHL16c is the default 53 | deco_model = ZHL16b 54 | 55 | # ppo2 56 | # defines the max and min_ppo2 57 | # default values : 58 | # max_ppo2 : 1.6 59 | # min_ppo2 : 0.21 60 | max_ppo2 = 1.5 61 | min_ppo2 = 0.19 62 | 63 | # max end 64 | # defines the max END for the dives 65 | # default value (in meter): 66 | # max_end : 30 67 | max_end = 33 68 | 69 | # decent and ascent rate, in m/minute 70 | descent_rate = 32 71 | ascent_rate = 8 72 | deco_ascent_rate = 4 73 | 74 | # Gradient factors in % 75 | gf_low = 35 76 | gf_high = 85 77 | 78 | # type of water 79 | # possible values : 80 | # sea -- sea water 81 | # fresh -- fresh water 82 | water = fresh 83 | 84 | # dive altitude 85 | # in meter 86 | altitude = 1234 87 | 88 | # consumption rates 89 | # in liter / minute (the program does the conversion internally) 90 | dive_consumption_rate = 27 91 | deco_consumption_rate = 22 92 | 93 | # run_time flag 94 | # if true: segments represents runtime, 95 | # if false, segments represents segtime 96 | run_time = false 97 | 98 | # Use Open Circuit deco flag 99 | # if True, use enabled gases of decomp in oc or bailout 100 | use_oc_deco = false 101 | 102 | # multilevel_mode 103 | multilevel_mode = true 104 | 105 | # automatic_tank_refill 106 | # if 'automatic_tank_refill' is set to True, the tank will be full before the 107 | # dive. If set to False, it'll use the remaining gas from last dive 108 | automatic_tank_refill = false 109 | 110 | # ========================== Advanced parameters ============================== 111 | # ========================= "Internal" Settings ================================ 112 | # !!! unless knowing what you're doing, this prefs should not be changed !!! 113 | # !!! by the user !!! 114 | # ============================================================================== 115 | [advanced] 116 | # water density kg/l 117 | fresh_water_density = 0.999 118 | sea_water_density = 1.099 119 | absolute_max_ppo2 = 1.999 120 | absolute_min_ppo2 = 0.1599 121 | 122 | # in bar 123 | absolute_max_tank_pressure = 299 124 | 125 | # in liter 126 | absolute_max_tank_size = 39 127 | 128 | # temperature at surface 129 | surface_temp = 29 130 | 131 | he_narcotic_value = 0.29 132 | n2_narcotic_value = 0.99 133 | o2_narcotic_value = 0.99 134 | ar_narcotic_value = 2.299 135 | 136 | # in meter 137 | stop_depth_increment = 2 138 | 139 | # in meter : last stop before surfacing 140 | last_stop_depth = 2 141 | 142 | # in second 143 | stop_time_increment = 5 144 | 145 | # one deco stop begun, force to stop to each deco depth 146 | # stop 147 | force_all_stops = false 148 | 149 | # surface pressure at sea level (in bar) 150 | ambiant_pressure_sea_level = 1.099 151 | 152 | # either simple (/10) or complex 153 | method_for_depth_calculation = simple 154 | 155 | # travel switch method 156 | # if 'late', dipplanner will try to keep the travel as long as possible 157 | # until either MOD or max END 158 | # if 'early', dipplanner will switch to bottom tank as soon as is it breathable 159 | travel_switch = early 160 | 161 | # flight altitude 162 | # parameter used in no flight time calculation : it's the parameter needed 163 | # to calculate decompression until the altitude of the flight 164 | # the default value represents the maximum 'altitude equivalent' tolerated 165 | # in flight by international regulation 166 | # (8000 feet = 2 438.4 meters rounded to 2450m) 167 | flight_altitude = 2699 168 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. linkcheck to check all external links for integrity 37 | echo. doctest to run all doctests embedded in the documentation if enabled 38 | goto end 39 | ) 40 | 41 | if "%1" == "clean" ( 42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 43 | del /q /s %BUILDDIR%\* 44 | goto end 45 | ) 46 | 47 | if "%1" == "html" ( 48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 49 | if errorlevel 1 exit /b 1 50 | echo. 51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 52 | goto end 53 | ) 54 | 55 | if "%1" == "dirhtml" ( 56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 57 | if errorlevel 1 exit /b 1 58 | echo. 59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 60 | goto end 61 | ) 62 | 63 | if "%1" == "singlehtml" ( 64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 68 | goto end 69 | ) 70 | 71 | if "%1" == "pickle" ( 72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished; now you can process the pickle files. 76 | goto end 77 | ) 78 | 79 | if "%1" == "json" ( 80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished; now you can process the JSON files. 84 | goto end 85 | ) 86 | 87 | if "%1" == "htmlhelp" ( 88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can run HTML Help Workshop with the ^ 92 | .hhp project file in %BUILDDIR%/htmlhelp. 93 | goto end 94 | ) 95 | 96 | if "%1" == "qthelp" ( 97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 98 | if errorlevel 1 exit /b 1 99 | echo. 100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 101 | .qhcp project file in %BUILDDIR%/qthelp, like this: 102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\dipplanner.qhcp 103 | echo.To view the help file: 104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\dipplanner.ghc 105 | goto end 106 | ) 107 | 108 | if "%1" == "devhelp" ( 109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished. 113 | goto end 114 | ) 115 | 116 | if "%1" == "epub" ( 117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 121 | goto end 122 | ) 123 | 124 | if "%1" == "latex" ( 125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 129 | goto end 130 | ) 131 | 132 | if "%1" == "text" ( 133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The text files are in %BUILDDIR%/text. 137 | goto end 138 | ) 139 | 140 | if "%1" == "man" ( 141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 145 | goto end 146 | ) 147 | 148 | if "%1" == "texinfo" ( 149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 150 | if errorlevel 1 exit /b 1 151 | echo. 152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 153 | goto end 154 | ) 155 | 156 | if "%1" == "gettext" ( 157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 158 | if errorlevel 1 exit /b 1 159 | echo. 160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 161 | goto end 162 | ) 163 | 164 | if "%1" == "changes" ( 165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 166 | if errorlevel 1 exit /b 1 167 | echo. 168 | echo.The overview file is in %BUILDDIR%/changes. 169 | goto end 170 | ) 171 | 172 | if "%1" == "linkcheck" ( 173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 174 | if errorlevel 1 exit /b 1 175 | echo. 176 | echo.Link check complete; look for any errors in the above output ^ 177 | or in %BUILDDIR%/linkcheck/output.txt. 178 | goto end 179 | ) 180 | 181 | if "%1" == "doctest" ( 182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 183 | if errorlevel 1 exit /b 1 184 | echo. 185 | echo.Testing of doctests in the sources finished, look at the ^ 186 | results in %BUILDDIR%/doctest/output.txt. 187 | goto end 188 | ) 189 | 190 | :end 191 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/dipplanner.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/dipplanner.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/dipplanner" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/dipplanner" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /dipplanner/tests/model_buhlmann_compartement_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test for compartment class.""" 23 | import unittest 24 | # import here the module / classes to be tested 25 | from dipplanner.main import activate_debug_for_tests 26 | 27 | from dipplanner.model.buhlmann.compartment import Compartment 28 | from dipplanner import settings 29 | 30 | 31 | class TestModelBuhlmannCompartemnt(unittest.TestCase): 32 | """Test compartements of the buhlmann model.""" 33 | 34 | def setUp(self): 35 | """Init of the tests.""" 36 | super().setUp() 37 | # temporary hack (tests): 38 | activate_debug_for_tests() 39 | settings.RUN_TIME = True 40 | settings.SURFACE_TEMP = 12 41 | self.compt1 = Compartment(1.88, 5.0, 42 | 1.6189, 0.4770, 43 | 1.1696, 0.5578) 44 | self.compt2 = Compartment(1.88, 5.0, 45 | 1.6189, 0.4770, 46 | 1.1696, 0.5578) 47 | self.compt2.set_pp(0.3 * 5, (1 - 0.21 - 0.3) * 5) 48 | self.compt3 = Compartment(1.88, 5.0, 49 | 1.6189, 0.4770, 50 | 1.1696, 0.5578) 51 | self.compt3.set_pp(0.0, 3.16) 52 | 53 | def test_compartment_simple_1(self): 54 | """test_compartment_simple_1.""" 55 | self.assertEqual(self.compt1.pp_he, 0, 56 | "wrong pp_he : %s" % self.compt1.pp_he) 57 | 58 | def test_compartment_simple_2(self): 59 | """test_compartment_simple_2.""" 60 | self.assertEqual(self.compt1.pp_n2, 0, 61 | "wrong pp_n2 : %s" % self.compt1.pp_n2) 62 | 63 | def test_compartment_simple_3(self): 64 | """test_compartment_simple_3.""" 65 | self.assertEqual(round(self.compt1.k_he, 14), 0.00614492181347, 66 | "wrong k_he : %s" % self.compt1.k_he) 67 | 68 | def test_compartment_simple_4(self): 69 | """test_compartment_simple_4.""" 70 | self.assertEqual(round(self.compt1.k_n2, 14), 0.00231049060187, 71 | "wrong k_n2 : %s" % self.compt1.k_n2) 72 | 73 | def test_compartment_simple_5(self): 74 | """test_compartment_simple_5.""" 75 | self.assertEqual(self.compt1.a_he, 1.6189, 76 | "wrong a_he : %s" % self.compt1.a_he) 77 | 78 | def test_compartment_simple_6(self): 79 | """test_compartment_simple_6.""" 80 | self.assertEqual(self.compt1.b_he, 0.4770, 81 | "wrong b_he : %s" % self.compt1.b_he) 82 | 83 | def test_compartment_simple_7(self): 84 | """test_compartment_simple_7.""" 85 | self.assertEqual(self.compt1.a_n2, 1.1696, 86 | "wrong a_n2 : %s" % self.compt1.a_n2) 87 | 88 | def test_compartment_simple_8(self): 89 | """test_compartment_simple_8.""" 90 | self.assertEqual(self.compt1.b_n2, 0.5578, 91 | "wrong b_n2 : %s" % self.compt1.b_n2) 92 | 93 | def test_compartment_simple_9(self): 94 | """test_compartment_simple_9.""" 95 | self.assertEqual(self.compt2.pp_he, 1.5, 96 | "wrong pp_he : %s" % self.compt2.pp_he) 97 | 98 | def test_compartment_simple_10(self): 99 | """test_compartment_simple_10.""" 100 | self.assertEqual(self.compt2.pp_n2, 2.45, 101 | "wrong pp_n2 : %s" % self.compt2.pp_n2) 102 | 103 | def test_compartment_simple_11(self): 104 | """test_compartment_simple_11.""" 105 | self.compt2.const_depth(0.3 * 4.5, (1 - 0.21 - 0.3) * 4.5, 12 * 60) 106 | self.assertEqual(round(self.compt2.pp_he, 11), 1.35179731087, 107 | "wrong pp_he : %s" % self.compt2.pp_he) 108 | 109 | def test_compartment_simple_12(self): 110 | """test_compartment_simple_12.""" 111 | self.compt2.const_depth(0.3 * 4.5, (1 - 0.21 - 0.3) * 4.5, 12 * 60) 112 | self.assertEqual(round(self.compt2.pp_n2, 11), 2.25141881985, 113 | "wrong pp_n2 : %s" % self.compt2.pp_n2) 114 | 115 | def test_compartment_simple_13(self): 116 | """test_compartment_simple_13.""" 117 | self.compt2.asc_desc(0.2997, 0.48951, 0.1, 0.163333333333, 9.0) 118 | self.assertEqual(round(self.compt2.pp_he, 11), 1.45985489718, 119 | "wrong pp_he : %s" % self.compt2.pp_he) 120 | 121 | def test_compartment_simple_14(self): 122 | """test_compartment_simple_14.""" 123 | self.compt2.asc_desc(0.2997, 0.48951, 0.1, 0.163333333333, 9.0) 124 | self.assertEqual(round(self.compt2.pp_n2, 11), 2.42483220311, 125 | "wrong pp_n2 : %s" % self.compt2.pp_n2) 126 | 127 | def test_compartment_mvalue_1(self): 128 | """test_compartment_mvalue_1.""" 129 | mv = self.compt3.get_m_value_at(0.0) 130 | self.assertEqual(mv, 1.1696, "wrong M-Value : %s" % mv) 131 | 132 | def test_compartment_mvalue_2(self): 133 | """test_compartment_mvalue_2.""" 134 | mv = self.compt3.get_m_value_at(1.0) 135 | self.assertEqual(round(mv, 11), 2.96235726067, 136 | "wrong M-Value : %s" % mv) 137 | 138 | def test_compartment_mvalue_3(self): 139 | """test_compartment_mvalue_3.""" 140 | mv = self.compt3.get_m_value_at(3.0) 141 | self.assertEqual(round(mv, 9), 6.547871782, 142 | "wrong M-Value : %s" % mv) 143 | 144 | def test_compartment_maxamb_1(self): 145 | """test_compartment_maxamb 1.""" 146 | max_amb = self.compt3.get_max_amb(0.8) 147 | self.assertEqual(round(max_amb, 11), 1.36110151389, 148 | "wrong max_amb for given gf : %s" % max_amb) 149 | 150 | def test_compartment_mv_1(self): 151 | """test_compartment_mv 1.""" 152 | mv = self.compt3.get_mv(1.0) 153 | self.assertEqual(round(mv, 11), 1.06671806333, 154 | "wrong mv for given amb pressure : %s" % mv) 155 | -------------------------------------------------------------------------------- /dipplanner/tests/segment_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test for Segment class.""" 23 | import unittest 24 | # import here the module / classes to be tested 25 | from dipplanner.main import activate_debug_for_tests 26 | 27 | from dipplanner.tank import Tank 28 | from dipplanner.segment import SegmentDive, SegmentDeco, SegmentAscDesc 29 | from dipplanner.segment import UnauthorizedMod 30 | from dipplanner import settings 31 | 32 | 33 | class TestSegment(unittest.TestCase): 34 | """Test Segment base class.""" 35 | 36 | def setUp(self): 37 | """Init of the tests.""" 38 | super().setUp() 39 | # temporary hack (tests): 40 | activate_debug_for_tests() 41 | settings.RUN_TIME = True 42 | settings.SURFACE_TEMP = 12 43 | settings.DIVE_CONSUMPTION_RATE = 17.0 / 60 #: liter/s 44 | settings.DECO_CONSUMPTION_RATE = 12.0 / 60 #: liter/s 45 | self.airtank = Tank() 46 | self.trimixtank1 = Tank(f_o2=0.10, f_he=0.70) 47 | self.nitroxtank1 = Tank(f_o2=0.40) 48 | self.oxygentank1 = Tank(f_o2=1) 49 | self.diveseg1 = SegmentDive(30, 10 * 60, self.airtank, 0) 50 | self.diveseg2 = SegmentDive(150, 10 * 60, self.trimixtank1, 0) 51 | self.decoseg1 = SegmentDeco(12, 5 * 60, self.nitroxtank1, 0) 52 | self.decoseg2 = SegmentDeco(3, 15 * 60, self.oxygentank1, 0) 53 | self.ascseg1 = SegmentAscDesc(150, 50, 20.0 / 60, self.trimixtank1, 0) 54 | self.ascseg2 = SegmentAscDesc(30, 12, 10.0 / 60, self.nitroxtank1, 0) 55 | self.descseg1 = SegmentAscDesc(0, 40, 20.0 / 60, self.airtank, 0) 56 | self.descseg2 = SegmentAscDesc(40, 150, 20.0 / 60, self.trimixtank1, 0) 57 | 58 | def tearDown(self): 59 | """Restore some settings.""" 60 | settings.RUN_TIME = True 61 | settings.SURFACE_TEMP = 20 62 | settings.DIVE_CONSUMPTION_RATE = 20.0 / 60 #: liter/s 63 | settings.DECO_CONSUMPTION_RATE = 17.0 / 60 #: liter/s 64 | 65 | def test_gas_used_1(self): 66 | """test_gas_used 1.""" 67 | self.assertAlmostEqual(self.diveseg1.gas_used, 687.5718, 4, 68 | 'Wrong gas used : %s' 69 | % self.diveseg1.gas_used) 70 | 71 | def test_gas_end_1(self): 72 | """test_gas_end 1.""" 73 | self.assertAlmostEqual(self.diveseg1.end, 74 | 29.0057341025, 5, 75 | 'wrong E.N.D : %s' % self.diveseg1.end) 76 | 77 | def test_gas_end_2(self): 78 | """test_gas_end 2.""" 79 | self.assertAlmostEqual(self.diveseg2.end, 80 | 61.912489531, 5, 81 | 'wrong E.N.D : %s' % self.diveseg2.end) 82 | 83 | def test_str_1(self): 84 | """test_str 1.""" 85 | self.assertEqual( 86 | str(self.diveseg2), 87 | " CONST: at 150m for 10:00 [RT: 0:00], on Trimix 10/70, " 88 | "SP:0.0, END:61m", 89 | 'wrong name : %s' % str(self.diveseg2)) 90 | 91 | def test_deco_1(self): 92 | """test deco seg gas used 1.""" 93 | self.assertAlmostEqual(self.decoseg1.gas_used, 94 | 133.54596, 5, 95 | 'Wrong gas used : %s' 96 | % self.decoseg1.gas_used) 97 | 98 | def test_deco_2(self): 99 | """test deco seg gas used 2.""" 100 | self.assertAlmostEqual(self.decoseg2.gas_used, 101 | 236.94822, 5, 'Wrong gas used : %s' 102 | % self.decoseg2.gas_used) 103 | 104 | def test_asc_1(self): 105 | """test asc seg gas used 2.""" 106 | self.assertAlmostEqual(self.ascseg1.gas_used, 107 | 944.99175, 5, 'Wrong gas used : %s' 108 | % self.ascseg1.gas_used) 109 | 110 | def test_asc_2(self): 111 | """test asc seg gas used 2.""" 112 | self.assertAlmostEqual(self.ascseg2.gas_used, 113 | 95.9356818, 7, 'Wrong gas used : %s' 114 | % self.ascseg2.gas_used) 115 | 116 | def test_desc_1(self): 117 | """test desc seg gas used 2.""" 118 | self.assertAlmostEqual(self.descseg1.gas_used, 119 | 103.15974, 5, 'Wrong gas used : %s' 120 | % self.descseg1.gas_used) 121 | 122 | def test_desc_2(self): 123 | """test desc seg gas used 2.""" 124 | self.assertAlmostEqual(self.descseg2.gas_used, 125 | 992.2533225, 7, 126 | 'Wrong gas used : %s' 127 | % self.descseg2.gas_used) 128 | 129 | def test_wrong_mod_1(self): 130 | """test wrong mod 1.""" 131 | try: 132 | baddiveseg = SegmentDive(150, 10 * 60, self.airtank, 0) 133 | baddiveseg.check() 134 | except UnauthorizedMod: 135 | pass 136 | else: 137 | self.fail("should raise UnauthorizedMod") 138 | 139 | def test_wrong_mod_2(self): 140 | """test wrong mod 2.""" 141 | try: 142 | baddiveseg = SegmentDeco(3, 10 * 60, self.trimixtank1, 0) 143 | baddiveseg.check() 144 | except UnauthorizedMod: 145 | pass 146 | else: 147 | self.fail("should raise UnauthorizedMod") 148 | 149 | def test_wrong_mod_3(self): 150 | """test wrong mod 3.""" 151 | try: 152 | baddiveseg = SegmentAscDesc(150, 3, 10, self.nitroxtank1, 0) 153 | baddiveseg.check() 154 | except UnauthorizedMod: 155 | pass 156 | else: 157 | self.fail("should raise UnauthorizedMod") 158 | 159 | def test_wrong_mod_4(self): 160 | """test wrong mod 4.""" 161 | try: 162 | baddiveseg = SegmentAscDesc(3, 150, 10, self.trimixtank1, 0) 163 | baddiveseg.check() 164 | except UnauthorizedMod: 165 | pass 166 | else: 167 | self.fail("should raise UnauthorizedMod") 168 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_cc_air_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test Dives with air.""" 23 | import json 24 | import pkg_resources 25 | 26 | from dipplanner.dive import Dive 27 | from dipplanner.segment import SegmentDive 28 | from dipplanner.segment import UnauthorizedMod 29 | from dipplanner import settings 30 | 31 | from dipplanner.tests.common import TestDive, TMethodsMixin 32 | 33 | 34 | # ============================================================================ 35 | # ======= S Y S T E M A T I C T E S T S =============================== 36 | # ============================================================================ 37 | class TestDiveCCAirBase(TestDive): 38 | """Class for test air dive.""" 39 | 40 | def setUp(self): 41 | """Init of the tests.""" 42 | super().setUp() 43 | 44 | # load json results file 45 | self.results = json.loads( 46 | pkg_resources.resource_string( 47 | "dipplanner.tests", 48 | __name__.split('.')[-1] + '.json').decode('utf-8')) 49 | 50 | 51 | class TestDiveCCAir(TestDiveCCAirBase): 52 | """Class for test air dive.""" 53 | 54 | def setUp(self): 55 | """Init of the tests.""" 56 | super().setUp() 57 | 58 | settings.RUN_TIME = False 59 | self.setpoint = 1.2 60 | self.dive_tank = self.ccair 61 | self.all_tanks = [self.ccair] 62 | self.do_dive() 63 | 64 | 65 | # AIR ========================================================================= 66 | # =============================================s====== 10m tests ============== 67 | class TestDiveCCAir10m10min(TestDiveCCAir, TMethodsMixin): 68 | """Test air 10m 10min.""" 69 | 70 | params = ((10, 10), ) 71 | 72 | 73 | 74 | class TestDiveCCAir10m20min(TestDiveCCAir, TMethodsMixin): 75 | """Test air 10m 20min.""" 76 | 77 | params = ((10, 20), ) 78 | 79 | 80 | class TestDiveCCAir10m30min(TestDiveCCAir, TMethodsMixin): 81 | """Test air 10m 30min.""" 82 | 83 | params = ((10, 30), ) 84 | 85 | 86 | class TestDiveCCAir10m40min(TestDiveCCAir, TMethodsMixin): 87 | """Test air 10m 40min.""" 88 | 89 | params = ((10, 40), ) 90 | 91 | 92 | class TestDiveCCAir10m50min(TestDiveCCAir, TMethodsMixin): 93 | """Test air 10m 50min.""" 94 | 95 | params = ((10, 50), ) 96 | 97 | 98 | class TestDiveCCAir10m60min(TestDiveCCAir, TMethodsMixin): 99 | """Test air 10m 60min.""" 100 | 101 | params = ((10, 60), ) 102 | 103 | 104 | class TestDiveCCAir10m70min(TestDiveCCAir, TMethodsMixin): 105 | """Test air 10m 70min.""" 106 | 107 | params = ((10, 70), ) 108 | 109 | 110 | # ==================================================== 20m tests ============== 111 | class TestDiveCCAir20m10min(TestDiveCCAir, TMethodsMixin): 112 | """Test air 20m 10min.""" 113 | 114 | params = ((20, 10), ) 115 | 116 | class TestDiveCCAir20m20min(TestDiveCCAir, TMethodsMixin): 117 | """Test air 20m 20min.""" 118 | 119 | params = ((20, 20), ) 120 | 121 | class TestDiveCCAir20m30min(TestDiveCCAir, TMethodsMixin): 122 | """Test air 20m 30min.""" 123 | 124 | params = ((20, 30), ) 125 | 126 | class TestDiveCCAir20m40min(TestDiveCCAir, TMethodsMixin): 127 | """Test air 20m 40min.""" 128 | 129 | params = ((20, 40), ) 130 | 131 | class TestDiveCCAir20m50min(TestDiveCCAir, TMethodsMixin): 132 | """Test air 20m 50min.""" 133 | 134 | params = ((20, 50), ) 135 | 136 | 137 | # ==================================================== 30m tests ============== 138 | class TestDiveCCAir30m10min(TestDiveCCAir, TMethodsMixin): 139 | """Test air 30m 10min.""" 140 | 141 | params = ((30, 10), ) 142 | 143 | 144 | class TestDiveCCAir30m20min(TestDiveCCAir, TMethodsMixin): 145 | """Test air 30m 20min.""" 146 | 147 | params = ((30, 20), ) 148 | 149 | 150 | class TestDiveCCAir30m30min(TestDiveCCAir, TMethodsMixin): 151 | """Test air 30m 30min.""" 152 | 153 | params = ((30, 30), ) 154 | 155 | class TestDiveCCAir30m40min(TestDiveCCAir, TMethodsMixin): 156 | """Test air 30m 40min.""" 157 | 158 | params = ((30, 40), ) 159 | 160 | class TestDiveCCAir30m50min(TestDiveCCAir, TMethodsMixin): 161 | """Test air 30m 50min.""" 162 | 163 | params = ((30, 50), ) 164 | 165 | # ==================================================== 40m tests ============== 166 | class TestDiveCCAir40m10min(TestDiveCCAir, TMethodsMixin): 167 | """Test air 40m 10min.""" 168 | 169 | params = ((40, 10), ) 170 | 171 | class TestDiveCCAir40m20min(TestDiveCCAir, TMethodsMixin): 172 | """Test air 40m 20min.""" 173 | 174 | params = ((40, 20), ) 175 | 176 | 177 | class TestDiveCCAir40m30min(TestDiveCCAir, TMethodsMixin): 178 | """Test air 40m 30min.""" 179 | 180 | params = ((40, 30), ) 181 | 182 | class TestDiveCCAir40m40min(TestDiveCCAir, TMethodsMixin): 183 | """Test air 40m 40min.""" 184 | 185 | params = ((40, 40), ) 186 | 187 | class TestDiveCCAir40m50min(TestDiveCCAir, TMethodsMixin): 188 | """Test air 40m 50min.""" 189 | 190 | params = ((40, 50), ) 191 | 192 | # ==================================================== 70m tests ============== 193 | class TestDiveCCAir50m10min(TestDive): 194 | """Test air 70m 10min.""" 195 | 196 | params = ((50, 10), ) 197 | 198 | def runTest(self): 199 | """Run one test.""" 200 | try: 201 | self.setpoint = 1.2 202 | self.dive_tank = self.ccair 203 | self.all_tanks = [self.ccair] 204 | diveseg1 = SegmentDive(self.params[0][0], self.params[0][1] * 60, 205 | self.dive_tank, self.setpoint) 206 | self.profile1 = Dive([diveseg1], self.all_tanks) 207 | self.profile1.do_dive() 208 | except UnauthorizedMod: 209 | pass 210 | else: 211 | self.fail("should raise UnauthorizedMod") 212 | 213 | 214 | # ======================= Multilevel Dive ===================================== 215 | class TestDiveCCAirMultilevel1(TestDiveCCAir, TMethodsMixin): 216 | """Multilevel dive test.""" 217 | 218 | params = ((40, 10), (45, 12), (30, 15)) 219 | name = 'multilevel1' 220 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_cc_txhypo_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test Dives in hypoxic trimix.""" 23 | import json 24 | import pkg_resources 25 | 26 | from dipplanner.dive import Dive 27 | from dipplanner.segment import SegmentDive 28 | from dipplanner.segment import UnauthorizedMod 29 | from dipplanner import settings 30 | 31 | from dipplanner.tests.common import TestDive, TMethodsMixin 32 | 33 | 34 | class TestDiveCCTxHypoBase(TestDive): 35 | """Class for test air dive.""" 36 | 37 | def setUp(self): 38 | """Init of the tests.""" 39 | super().setUp() 40 | 41 | # load json results file 42 | self.results = json.loads( 43 | pkg_resources.resource_string( 44 | "dipplanner.tests", 45 | __name__.split('.')[-1] + '.json').decode('utf-8')) 46 | 47 | 48 | class TestDiveCCTxHypo(TestDiveCCTxHypoBase): 49 | """Class for test air dive.""" 50 | 51 | def setUp(self): 52 | """Init of the tests.""" 53 | super().setUp() 54 | 55 | settings.RUN_TIME = False 56 | self.setpoint = 1.2 57 | self.dive_tank = self.cctxhypo 58 | self.all_tanks = [self.cctxhypo] 59 | self.do_dive() 60 | 61 | 62 | # TxHypo 10/50 + tavel Tx21/30 + DECO Nx80 ==================================== 63 | # ==================================================== 50m tests ============== 64 | class TestDiveCCTxHypo50m10min(TestDiveCCTxHypo, TMethodsMixin): 65 | """Test tx hypo 50m 10min.""" 66 | 67 | params = ((50, 10), ) 68 | 69 | 70 | class TestDiveCCTxHypo50m20min(TestDiveCCTxHypo, TMethodsMixin): 71 | """Test tx hypo 50m 20min.""" 72 | 73 | params = ((50, 20), ) 74 | 75 | 76 | class TestDiveCCTxHypo50m30min(TestDiveCCTxHypo, TMethodsMixin): 77 | """Test tx hypo 50m 30min.""" 78 | 79 | params = ((50, 30), ) 80 | 81 | 82 | class TestDiveCCTxHypo50m40min(TestDiveCCTxHypo, TMethodsMixin): 83 | """Test tx hypo 50m 40min.""" 84 | 85 | params = ((50, 40), ) 86 | 87 | 88 | class TestDiveCCTxHypo50m50min(TestDiveCCTxHypo, TMethodsMixin): 89 | """Test tx hypo 50m 50min.""" 90 | 91 | params = ((50, 50), ) 92 | 93 | 94 | # ==================================================== 60m tests ============== 95 | class TestDiveCCTxHypo60m10min(TestDiveCCTxHypo, TMethodsMixin): 96 | """Test tx hypo 60m 10min.""" 97 | 98 | params = ((60, 10), ) 99 | 100 | 101 | class TestDiveCCTxHypo60m20min(TestDiveCCTxHypo, TMethodsMixin): 102 | """Test tx hypo 60m 20min.""" 103 | 104 | params = ((60, 20), ) 105 | 106 | 107 | class TestDiveCCTxHypo60m30min(TestDiveCCTxHypo, TMethodsMixin): 108 | """Test tx hypo 60m 30min.""" 109 | 110 | params = ((60, 30), ) 111 | 112 | 113 | class TestDiveCCTxHypo60m40min(TestDiveCCTxHypo, TMethodsMixin): 114 | """Test tx hypo 60m 40min.""" 115 | 116 | params = ((60, 40), ) 117 | 118 | # ==================================================== 70m tests ============== 119 | class TestDiveCCTxHypo70m10min(TestDiveCCTxHypo, TMethodsMixin): 120 | """Test tx hypo 70m 10min.""" 121 | 122 | params = ((70, 10), ) 123 | 124 | 125 | class TestDiveCCTxHypo70m20min(TestDiveCCTxHypo, TMethodsMixin): 126 | """Test tx hypo 70m 20min.""" 127 | 128 | params = ((70, 20), ) 129 | 130 | 131 | class TestDiveCCTxHypo70m30min(TestDiveCCTxHypo, TMethodsMixin): 132 | """Test tx hypo 70m 30min.""" 133 | 134 | params = ((70, 30), ) 135 | 136 | 137 | # ==================================================== 80m tests ============== 138 | class TestDiveCCTxHypo80m10min(TestDiveCCTxHypo, TMethodsMixin): 139 | """Test tx hypo 80m 10min.""" 140 | 141 | params = ((80, 10), ) 142 | 143 | 144 | class TestDiveCCTxHypo80m20min(TestDiveCCTxHypo, TMethodsMixin): 145 | """Test tx hypo 80m 20min.""" 146 | 147 | params = ((80, 20), ) 148 | 149 | 150 | class TestDiveCCTxHypo80m30min(TestDiveCCTxHypo, TMethodsMixin): 151 | """Test tx hypo 80m 30min.""" 152 | 153 | params = ((80, 30), ) 154 | 155 | # ==================================================== 90m tests ============== 156 | class TestDiveCCTxHypo90m10min(TestDiveCCTxHypo, TMethodsMixin): 157 | """Test tx hypo 90m 10min.""" 158 | 159 | params = ((90, 10), ) 160 | 161 | 162 | class TestDiveCCTxHypo90m20min(TestDiveCCTxHypo, TMethodsMixin): 163 | """Test tx hypo 90m 20min.""" 164 | 165 | params = ((90, 20), ) 166 | 167 | 168 | class TestDiveCCTxHypo90m30min(TestDiveCCTxHypo, TMethodsMixin): 169 | """Test tx hypo 90m 30min.""" 170 | 171 | params = ((90, 30), ) 172 | 173 | 174 | # =================================================== 100m tests ============== 175 | class TestDiveCCTxHypo100m5min(TestDiveCCTxHypo, TMethodsMixin): 176 | """Test tx hypo 100m 5min.""" 177 | 178 | params = ((100, 5), ) 179 | 180 | class TestDiveCCTxHypo100m10min(TestDiveCCTxHypo, TMethodsMixin): 181 | """Test tx hypo 100m 10min.""" 182 | 183 | params = ((100, 10), ) 184 | 185 | 186 | class TestDiveCCTxHypo100m15min(TestDiveCCTxHypo, TMethodsMixin): 187 | """Test tx hypo 100m 15min.""" 188 | 189 | params = ((100, 15), ) 190 | 191 | 192 | class TestDiveCCTxHypo100m20min(TestDiveCCTxHypo, TMethodsMixin): 193 | """Test tx hypo 100m 20min.""" 194 | 195 | params = ((100, 20), ) 196 | 197 | # =================================================== 160m tests ============== 198 | class TestDiveCCTxHypo110m10min(TestDive): 199 | """test CC tx hypo 110m 10min.""" 200 | 201 | params = ((110, 10), ) 202 | 203 | def runTest(self): 204 | """Should raise and error.""" 205 | try: 206 | self.setpoint = 1.2 207 | self.dive_tank = self.cctxhypo 208 | self.all_tanks = [self.cctxhypo] 209 | diveseg1 = SegmentDive(self.params[0][0], self.params[0][1] * 60, 210 | self.dive_tank, self.setpoint) 211 | self.profile1 = Dive([diveseg1], self.all_tanks) 212 | self.profile1.do_dive() 213 | except UnauthorizedMod: 214 | pass 215 | else: 216 | self.fail("should raise UnauthorizedMod") 217 | 218 | 219 | # ======================== Multilevel Dive ==================================== 220 | 221 | class TestDiveMultilevel(TestDiveCCTxHypo, TMethodsMixin): 222 | """Test tx hypo multilevel 1.""" 223 | 224 | params = ((40, 10), (50, 12), (30, 15)) 225 | name = 'multilevel1' 226 | 227 | -------------------------------------------------------------------------------- /docs/autodoc.rst: -------------------------------------------------------------------------------- 1 | .. _dipplanner_autodoc: 2 | 3 | Source code documentation 4 | ========================= 5 | 6 | .. _dipplanner_autodoc_dive: 7 | 8 | dive 9 | ---- 10 | 11 | .. automodule:: dipplanner.dive 12 | :members: 13 | :private-members: 14 | :special-members: 15 | :inherited-members: 16 | :member-order: bysource 17 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 18 | 19 | .. _dipplanner_autodoc_segment: 20 | 21 | segment 22 | ------- 23 | 24 | .. automodule:: dipplanner.segment 25 | :members: 26 | :private-members: 27 | :special-members: 28 | :inherited-members: 29 | :member-order: bysource 30 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 31 | 32 | .. _dipplanner_autodoc_tank: 33 | 34 | tank 35 | ---- 36 | 37 | .. automodule:: dipplanner.tank 38 | :members: 39 | :private-members: 40 | :special-members: 41 | :inherited-members: 42 | :member-order: bysource 43 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 44 | 45 | .. _dipplanner_autodoc_model_buhlmann: 46 | 47 | model: buhlmann 48 | ---------------- 49 | 50 | .. _dipplanner_autodoc_model_buhlmann_exceptions: 51 | 52 | Exceptions 53 | ^^^^^^^^^^ 54 | 55 | .. automodule:: dipplanner.model.buhlmann.model_exceptions 56 | :members: 57 | :private-members: 58 | :special-members: 59 | :inherited-members: 60 | :member-order: bysource 61 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 62 | 63 | .. _dipplanner_autodoc_model_buhlmann_compartment: 64 | 65 | compartment 66 | ^^^^^^^^^^^ 67 | 68 | .. automodule:: dipplanner.model.buhlmann.compartment 69 | :members: 70 | :private-members: 71 | :special-members: 72 | :inherited-members: 73 | :member-order: bysource 74 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 75 | 76 | .. _dipplanner_autodoc_model_buhlmann_gradient: 77 | 78 | gradient 79 | ^^^^^^^^ 80 | 81 | .. automodule:: dipplanner.model.buhlmann.gradient 82 | :members: 83 | :private-members: 84 | :special-members: 85 | :inherited-members: 86 | :member-order: bysource 87 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 88 | 89 | .. _dipplanner_autodoc_model_buhlmann_oxtox: 90 | 91 | oxygen toxicity 92 | ^^^^^^^^^^^^^^^ 93 | 94 | .. automodule:: dipplanner.model.buhlmann.oxygen_toxicity 95 | :members: 96 | :private-members: 97 | :special-members: 98 | :inherited-members: 99 | :member-order: bysource 100 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 101 | 102 | .. _dipplanner_autodoc_model_buhlmann_model: 103 | 104 | model 105 | ^^^^^ 106 | 107 | .. automodule:: dipplanner.model.buhlmann.model 108 | :members: 109 | :private-members: 110 | :special-members: 111 | :inherited-members: 112 | :member-order: bysource 113 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 114 | 115 | .. _dipplanner_autodoc_exceptions: 116 | 117 | Exceptions 118 | ---------- 119 | 120 | .. automodule:: dipplanner.dipp_exception 121 | :members: 122 | :private-members: 123 | :special-members: 124 | :inherited-members: 125 | :member-order: bysource 126 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 127 | 128 | .. _dipplanner_autodoc_model_tools: 129 | 130 | tools 131 | ----- 132 | 133 | .. automodule:: dipplanner.tools 134 | :members: 135 | :private-members: 136 | :special-members: 137 | :inherited-members: 138 | :member-order: bysource 139 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 140 | 141 | .. _dipplanner_autodoc_main: 142 | 143 | main 144 | ---- 145 | 146 | .. automodule:: dipplanner.main 147 | :members: 148 | :private-members: 149 | :special-members: 150 | :inherited-members: 151 | :member-order: bysource 152 | :exclude-members: __delattr__, __weakref__, __format__, __getattribute__, __hash__, __new__, __reduce__, __reduce_ex__, __setattr__, __sizeof__, __getitem__, __getslice__, __subclasshook__, __repr__ 153 | 154 | .. _dipplanner_autodoc_settings: 155 | 156 | settings 157 | -------- 158 | 159 | .. automodule:: dipplanner.settings 160 | 161 | .. autodata:: dipplanner.settings.FRESH_WATER_DENSITY 162 | .. autodata:: dipplanner.settings.SEA_WATER_DENSITY 163 | .. autodata:: dipplanner.settings.ABSOLUTE_MAX_PPO2 164 | .. autodata:: dipplanner.settings.ABSOLUTE_MIN_PPO2 165 | .. autodata:: dipplanner.settings.ABSOLUTE_MAX_TANK_PRESSURE 166 | .. autodata:: dipplanner.settings.ABSOLUTE_MAX_TANK_SIZE 167 | .. autodata:: dipplanner.settings.SURFACE_TEMP 168 | .. autodata:: dipplanner.settings.HE_NARCOTIC_VALUE 169 | .. autodata:: dipplanner.settings.N2_NARCOTIC_VALUE 170 | .. autodata:: dipplanner.settings.O2_NARCOTIC_VALUE 171 | .. autodata:: dipplanner.settings.AR_NARCOTIC_VALUE 172 | .. autodata:: dipplanner.settings.STOP_DEPTH_INCREMENT 173 | .. autodata:: dipplanner.settings.LAST_STOP_DEPTH 174 | .. autodata:: dipplanner.settings.STOP_TIME_INCREMENT 175 | .. autodata:: dipplanner.settings.FORCE_ALL_STOPS 176 | .. autodata:: dipplanner.settings.AMBIANT_PRESSURE_SEA_LEVEL 177 | .. autodata:: dipplanner.settings.METHOD_FOR_DEPTH_CALCULATION 178 | .. autodata:: dipplanner.settings.TRAVEL_SWITCH 179 | .. autodata:: dipplanner.settings.FLIGHT_ALTITUDE 180 | .. autodata:: dipplanner.settings.TEMPLATE 181 | .. autodata:: dipplanner.settings.DECO_MODEL 182 | .. autodata:: dipplanner.settings.WATER_DENSITY 183 | .. autodata:: dipplanner.settings.AMBIANT_PRESSURE_SURFACE 184 | .. autodata:: dipplanner.settings.DEFAULT_MAX_PPO2 185 | .. autodata:: dipplanner.settings.DEFAULT_MIN_PPO2 186 | .. autodata:: dipplanner.settings.DEFAULT_MAX_END 187 | .. autodata:: dipplanner.settings.DIVE_CONSUMPTION_RATE 188 | .. autodata:: dipplanner.settings.DECO_CONSUMPTION_RATE 189 | .. autodata:: dipplanner.settings.DESCENT_RATE 190 | .. autodata:: dipplanner.settings.ASCENT_RATE 191 | .. autodata:: dipplanner.settings.RUN_TIME 192 | .. autodata:: dipplanner.settings.USE_OC_DECO 193 | .. autodata:: dipplanner.settings.GF_LOW 194 | .. autodata:: dipplanner.settings.GF_HIGH 195 | .. autodata:: dipplanner.settings.MULTILEVEL_MODE 196 | .. autodata:: dipplanner.settings.AUTOMATIC_TANK_REFILL 197 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | dipplanner Quick Start 2 | ====================== 3 | 4 | .. warning:: 5 | 6 | This software is provided free of charge for experienced divers to evaluate diving profiles. 7 | This software is highly experimental, it probably contains bugs and should not be relied upon for actual dives. Use it at your own risk. 8 | 9 | If you follow dive schedules generated by this software you could suffer decompression sickness, serious injury or possibly death. 10 | The author does not warrant that this software accurately reflects Buhlmann's algorithms, or that it will produce safe, reliable, results. 11 | 12 | Diving in general is fraught with risk, and decompression diving using mixed gases and rebreathers adds significantly more risk. This software is not intended for uneducated users. 13 | This software and the decompression schedules it produces are tools for experienced divers only. 14 | 15 | IF YOU DO NOT UNDERSTAND OR DO NOT AGREE TO THIS STATEMENT DO NOT USE THIS SOFTWARE 16 | 17 | Description 18 | ----------- 19 | 20 | Model supported 21 | ^^^^^^^^^^^^^^^ 22 | 23 | * Bülhmann ZH-L16A with Gradient Factor 24 | * Bülhmann ZH-L16B with Gradient Factor 25 | * Bülhmann ZH-L16C with Gradient Factor 26 | 27 | for each, with 1a or 1b variation (4' or 5' quickest compartment) 28 | 29 | Gases 30 | ^^^^^ 31 | 32 | any mix of oxygen, helium and nitrogen is supported, this include: 33 | 34 | * Air 35 | * Nitrox 36 | * Oxygen 37 | * Trimix 38 | * Heliox 39 | * ... 40 | 41 | Other Features 42 | ^^^^^^^^^^^^^^ 43 | 44 | * Open Circuit Dives 45 | * CCR Dives 46 | 47 | * setpoint 48 | * bailout 49 | 50 | * Tank planification 51 | 52 | * Tank size and pressure 53 | * Simple or Double Tank 54 | * gaz consumption rate 55 | * rules for warning (rules of third, rules in remaining pressure, etc..) 56 | * automatic (or non automatic) refill of Tank between successive dives 57 | 58 | * Multigaz (multi Tank) 59 | * Automatic selection of best gaz 60 | * Multilevel Dives 61 | * Altitude Dives 62 | * Successive Dives 63 | * No Flight Time Calculation 64 | * CNS Tracking 65 | * OTU Tracking 66 | * MOD Calculation 67 | * END Calculation 68 | * Adjustable gradient factor and stop depth level 69 | 70 | Calculations 71 | ^^^^^^^^^^^^ 72 | 73 | The main principe of dipplanner calculation are to be as accurate as possible, 74 | using the most precise algorithm. 75 | 76 | * All internal calculation in SI Units 77 | * Most precise calculations (trying to avoid any approximation): 78 | 79 | * second to second decompression time calculation (adjustable) 80 | * real water density calculation 81 | * real gaz pressure calculation in tanks using van der waals 82 | * real surface pressure calculation (using Antoine equation for ppH2O) 83 | * ... 84 | 85 | * Every parameter is adjustable 86 | 87 | .. warning:: in result of the "precise" calculations, the planned dives may be 88 | shorter than other equivalent dive planification programs. 89 | 90 | Users of dipplanner MUST be aware of this difference and adjust 91 | the conservatisms to their wishes. 92 | 93 | On the other way, with the most precise calculations, dipplanner 94 | try to avoid unexpected effects (or worse: unseen effects) of 95 | approximations. (approximation effect to conservatism are sometimes 96 | counter-intuitive and may in some cases reduce the conservatism 97 | without warning) 98 | 99 | Get the sources 100 | --------------- 101 | 102 | latest source code (unstable) 103 | 104 | please use git to clone the repository : 105 | 106 | :: 107 | 108 | git clone git://github.com/ThomasChiroux/dipplanner.git 109 | 110 | Preparation: virtualvenv 111 | ^^^^^^^^^^^^^^^^^^^^^^^^ 112 | 113 | It is recommended (but optional) to setup a virtualvenv before running or installing. 114 | In this way, you will not break or change your real environment. 115 | So after getting the source, go in the dipplanner directory: 116 | 117 | dipplanner needs at least python 3.4 to work. 118 | 119 | :: 120 | 121 | cd dipplanner 122 | mkdir venv 123 | virtualenv venv 124 | source venv/bin/activate 125 | 126 | Installing 127 | ---------- 128 | 129 | Inside the dipplanner source directory, run: 130 | 131 | :: 132 | 133 | python setup.py install 134 | 135 | Using 136 | ----- 137 | 138 | This version if currently only usable in command line 139 | 140 | Run the program 141 | ^^^^^^^^^^^^^^^ 142 | 143 | ex: 144 | 145 | :: 146 | 147 | dipplanner --help 148 | 149 | 150 | Planning one dive 151 | ^^^^^^^^^^^^^^^^^ 152 | 153 | To plan a dive, you should at least provide one tank and one dive segment. 154 | Here is below a sample for a 12l tank with 200b of air and a dive of 25min at 30m 155 | 156 | :: 157 | 158 | dipplanner -t "airtank;0.21;0.0;12;200;50b" -s "30;25*60;airtank;0.0" 159 | 160 | You can provide more than one tank and of course multiple segments (they will be processed in the order you provided it) 161 | Deco tanks will be automaticaly choosen if appropriate. 162 | Here is below a sample for a trimix dive : bi 12l-cylinder of Tx21/30 and Deco Nx80 (S80), 50m - 20mins: 163 | 164 | :: 165 | 166 | dipplanner -t "tx;0.21;0.30;24;200;50b" -t "deco;0.8;0.0;12;200;50b" -s "50;20*60;tx;0.0" 167 | 168 | Change some parameters of the dive 169 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 170 | 171 | See :: 172 | 173 | dipplanner --help 174 | 175 | output to see all available parameters 176 | 177 | You can also read :ref:`dipplanner_cmdline` 178 | 179 | Config files 180 | ^^^^^^^^^^^^ 181 | 182 | You can provide one or several config files to the program. 183 | The config file can overide any default parameter. 184 | see config/default_config.cfg for all the details 185 | 186 | parameter are changed using this order: 187 | 188 | 1) default parameter 189 | 2) parameter set in config files 190 | 3) parameter set in command line 191 | 192 | You can also read :ref:`dipplanner_configfile` 193 | 194 | Units : SI or Imperial 195 | ---------------------- 196 | 197 | dipplanner uses only SI unit internally. 198 | However a config parameter or a command line parameter can change this behaviour. 199 | and use imperial Units 200 | 201 | if imperial unit is set in a config file : 202 | all the parameters from this config file and all the config files 203 | read after will be considered imperial (including command line parameters) 204 | But all the parameter in previous config files will still be considered as SI 205 | All the ouput will be done in imperial units 206 | 207 | if imperial unit is set in command line : 208 | all the parameter given in command line will be considered imperial, 209 | but not the parameters eventually given using config files 210 | All the output will be done in imperial units 211 | 212 | SI and imperial unit converter uses the following correspondances: 213 | 214 | * bar <--> psi 215 | * liter <--> cubic feet 216 | * meter <--> feet 217 | 218 | References 219 | ---------- 220 | 221 | * at first this program is a python rewrite of MV-Plan a dive planning tool written in java by Guy Wittig 222 | * `ref used for programming and understand algorithms `_. 223 | 224 | Open Source and Licence 225 | ----------------------- 226 | 227 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 228 | 229 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 230 | 231 | You should have received a copy of the GNU General Public License along with this program. 232 | If not, see http://www.gnu.org/licenses/gpl.html 233 | -------------------------------------------------------------------------------- /dipplanner/tests/model_buhlmann_model_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test for Model class.""" 23 | import unittest 24 | # import here the module / classes to be tested 25 | from dipplanner.main import activate_debug_for_tests 26 | 27 | from dipplanner import settings 28 | from dipplanner.model.buhlmann.model import Model 29 | from dipplanner.model.buhlmann.model_exceptions import ModelStateException 30 | 31 | 32 | class TestModelBuhlmannModel(unittest.TestCase): 33 | """Test the buhlmann model.""" 34 | 35 | def setUp(self): 36 | """Init of the tests.""" 37 | super().setUp() 38 | # temporary hack (tests): 39 | activate_debug_for_tests() 40 | settings.RUN_TIME = True 41 | settings.SURFACE_TEMP = 12 42 | # simples test 43 | self.model1 = Model() 44 | 45 | # OC tests 46 | self.model2 = Model() 47 | self.model2.const_depth(30, 12 * 60, 0.0, 0.79, 0.0) 48 | 49 | def test_model_units(self): 50 | """check units.""" 51 | self.assertEqual(self.model1.units, "metric", 52 | "Error in model unit : %s" % self.model1.units) 53 | 54 | def test_model_gf_low(self): 55 | """check gf low.""" 56 | self.assertEqual(self.model1.gradient.gf_low, settings.GF_LOW, 57 | "Error in model gradient gf low : %s" 58 | % self.model1.gradient.gf_low) 59 | 60 | def test_model_gf_high(self): 61 | """check gf high.""" 62 | self.assertEqual(self.model1.gradient.gf_high, settings.GF_HIGH, 63 | "Error in model gradient gf high : %s" 64 | % self.model1.gradient.gf_high) 65 | 66 | def test_model_gf(self): 67 | """check gf.""" 68 | self.assertEqual(self.model1.gradient.gf, settings.GF_LOW, 69 | "Error in model gradient gf : %s" 70 | % self.model1.gradient.gf) 71 | 72 | def test_metadata(self): 73 | """check metadata.""" 74 | self.assertEqual(self.model1.metadata, "(none)", 75 | "Error in model metadata : %s" 76 | % self.model1.metadata) 77 | 78 | def test_model_tissues(self): 79 | """check tissues.""" 80 | self.assertEqual(len(self.model1.tissues), 16, 81 | "Error in tissues number : %s" 82 | % len(self.model1.tissues)) 83 | 84 | def test_model_otu(self): 85 | """check otu.""" 86 | self.assertEqual(self.model1.ox_tox.otu, 0.0, 87 | "Error in model ox tox otu : %s" 88 | % self.model1.ox_tox.otu) 89 | 90 | def test_model_validation(self): 91 | """check validation.""" 92 | self.assertTrue( 93 | self.model1.validate_model(), 94 | "Error in model validation : %s" % self.model1.validate_model()) 95 | 96 | def test_model_control_compartment(self): 97 | """check control compartement.""" 98 | # empty model should have the first compartement 99 | # for control compartment ?? 100 | self.assertEqual(self.model1.control_compartment(), 1, 101 | "Error in control compartement : %s" 102 | % self.model1.control_compartment()) 103 | 104 | def test_model_ceiling(self): 105 | """check ceiling.""" 106 | self.assertEqual(self.model1.ceiling(), 0.0, 107 | "Error in model ceiling : %s" 108 | % self.model1.ceiling()) 109 | 110 | def test_model_mv(self): 111 | """check m value.""" 112 | mv = self.model1.m_value(12) 113 | self.assertAlmostEqual(mv, 0.0575659327931, 13, 114 | "Error in model m_value : %s" % mv) 115 | 116 | def test_model_output1(self): 117 | """check str output of model 1.""" 118 | self.assertEqual(str(self.model1), 119 | """C:0 He:0.000000 N2:0.789444 gf:0.30 mv_at:3.266336 max_amb:0.317972 MV:0.241691 120 | C:1 He:0.000000 N2:0.789444 gf:0.30 mv_at:2.555496 max_amb:0.421736 MV:0.308920 121 | C:2 He:0.000000 N2:0.789444 gf:0.30 mv_at:2.264805 max_amb:0.475978 MV:0.348571 122 | C:3 He:0.000000 N2:0.789444 gf:0.30 mv_at:2.051088 max_amb:0.519283 MV:0.384891 123 | C:4 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.866923 max_amb:0.564396 MV:0.422858 124 | C:5 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.705687 max_amb:0.604483 MV:0.462831 125 | C:6 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.606593 max_amb:0.628783 MV:0.491378 126 | C:7 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.537205 max_amb:0.645745 MV:0.513558 127 | C:8 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.489441 max_amb:0.657253 MV:0.530027 128 | C:9 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.448731 max_amb:0.667549 MV:0.544921 129 | C:10 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.416795 max_amb:0.675779 MV:0.557204 130 | C:11 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.384082 max_amb:0.684457 MV:0.570374 131 | C:12 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.352667 max_amb:0.692922 MV:0.583620 132 | C:13 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.322662 max_amb:0.701095 MV:0.596860 133 | C:14 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.303249 max_amb:0.706262 MV:0.605751 134 | C:15 He:0.000000 N2:0.789444 gf:0.30 mv_at:1.282374 max_amb:0.711956 MV:0.615612 135 | """, "Error in model output : %s" % str(self.model1)) 136 | 137 | def test_model_2_gf(self): 138 | """check gf of model 2.""" 139 | self.assertEqual(self.model2.gradient.gf, settings.GF_LOW, 140 | "Error in model gf : %s" % self.model2.gradient.gf) 141 | 142 | def test_model2_output_2(self): 143 | """check str output of model 1.""" 144 | self.assertEqual(str(self.model2), 145 | """C:0 He:0.000000 N2:21.526944 gf:0.30 mv_at:3.266336 max_amb:16.343125 MV:6.590549 146 | C:1 He:0.000000 N2:16.110229 gf:0.30 mv_at:2.555496 max_amb:13.623089 MV:6.304150 147 | C:2 He:0.000000 N2:12.306296 gf:0.30 mv_at:2.264805 max_amb:10.801312 MV:5.433712 148 | C:3 He:0.000000 N2:9.371747 gf:0.30 mv_at:2.051088 max_amb:8.441019 MV:4.569159 149 | C:4 He:0.000000 N2:7.073091 gf:0.30 mv_at:1.866923 max_amb:6.441438 MV:3.788635 150 | C:5 He:0.000000 N2:5.415924 gf:0.30 mv_at:1.705687 max_amb:4.986851 MV:3.175215 151 | C:6 He:0.000000 N2:4.155465 gf:0.30 mv_at:1.606593 max_amb:3.849531 MV:2.586508 152 | C:7 He:0.000000 N2:3.216158 gf:0.30 mv_at:1.537205 max_amb:2.986551 MV:2.092211 153 | C:8 He:0.000000 N2:2.530704 gf:0.30 mv_at:1.489441 max_amb:2.347861 MV:1.699096 154 | C:9 He:0.000000 N2:2.101916 gf:0.30 mv_at:1.448731 max_amb:1.947623 MV:1.450867 155 | C:10 He:0.000000 N2:1.820520 gf:0.30 mv_at:1.416795 max_amb:1.684736 MV:1.284957 156 | C:11 He:0.000000 N2:1.600073 gf:0.30 mv_at:1.384082 max_amb:1.479934 MV:1.156054 157 | C:12 He:0.000000 N2:1.427042 gf:0.30 mv_at:1.352667 max_amb:1.320136 MV:1.054984 158 | C:13 He:0.000000 N2:1.289557 gf:0.30 mv_at:1.322662 max_amb:1.194140 MV:0.974971 159 | C:14 He:0.000000 N2:1.182002 gf:0.30 mv_at:1.303249 max_amb:1.093999 MV:0.906966 160 | C:15 He:0.000000 N2:1.097863 gf:0.30 mv_at:1.282374 max_amb:1.017084 MV:0.856118 161 | """, "Error in model output : %s" % str(self.model2)) 162 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_air_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test Dives with air.""" 23 | import json 24 | import pkg_resources 25 | 26 | from dipplanner.dive import Dive 27 | from dipplanner.segment import SegmentDive 28 | from dipplanner.segment import UnauthorizedMod 29 | 30 | from dipplanner.tests.common import TestDive, TMethodsMixin 31 | 32 | 33 | # ============================================================================ 34 | # ======= S Y S T E M A T I C T E S T S =============================== 35 | # ============================================================================ 36 | class TestDiveAirBase(TestDive): 37 | """Class for test air dive.""" 38 | 39 | def setUp(self): 40 | """Init of the tests.""" 41 | super().setUp() 42 | 43 | # load json results file 44 | self.results = json.loads( 45 | pkg_resources.resource_string( 46 | "dipplanner.tests", 47 | __name__.split('.')[-1] + '.json').decode('utf-8')) 48 | 49 | 50 | class TestDiveAir(TestDiveAirBase): 51 | """Class for test air dive.""" 52 | 53 | def setUp(self): 54 | """Init of the tests.""" 55 | super().setUp() 56 | 57 | self.dive_tank = self.airtank12 58 | self.all_tanks = [self.airtank12] 59 | self.do_dive() 60 | 61 | 62 | class TestDiveAirMultilevel(TestDiveAirBase): 63 | """Multilevel dive test.""" 64 | 65 | def setUp(self): 66 | """Init of multilevel test.""" 67 | super().setUp() 68 | 69 | self.dive_tank = self.airdouble 70 | self.all_tanks = [self.airdouble] 71 | self.do_dive() 72 | 73 | 74 | # AIR ========================================================================= 75 | # =============================================s====== 10m tests ============== 76 | class TestDiveAir10m10min(TestDiveAir, TMethodsMixin): 77 | """Test air 10m 10min.""" 78 | 79 | params = ((10, 10), ) 80 | 81 | 82 | 83 | class TestDiveAir10m20min(TestDiveAir, TMethodsMixin): 84 | """Test air 10m 20min.""" 85 | 86 | params = ((10, 20), ) 87 | 88 | 89 | class TestDiveAir10m30min(TestDiveAir, TMethodsMixin): 90 | """Test air 10m 30min.""" 91 | 92 | params = ((10, 30), ) 93 | 94 | 95 | class TestDiveAir10m40min(TestDiveAir, TMethodsMixin): 96 | """Test air 10m 40min.""" 97 | 98 | params = ((10, 40), ) 99 | 100 | 101 | class TestDiveAir10m50min(TestDiveAir, TMethodsMixin): 102 | """Test air 10m 50min.""" 103 | 104 | params = ((10, 50), ) 105 | 106 | 107 | class TestDiveAir10m60min(TestDiveAir, TMethodsMixin): 108 | """Test air 10m 60min.""" 109 | 110 | params = ((10, 60), ) 111 | 112 | 113 | class TestDiveAir10m70min(TestDiveAir, TMethodsMixin): 114 | """Test air 10m 70min.""" 115 | 116 | params = ((10, 70), ) 117 | 118 | 119 | # ==================================================== 20m tests ============== 120 | class TestDiveAir20m10min(TestDiveAir, TMethodsMixin): 121 | """Test air 20m 10min.""" 122 | 123 | params = ((20, 10), ) 124 | 125 | class TestDiveAir20m20min(TestDiveAir, TMethodsMixin): 126 | """Test air 20m 20min.""" 127 | 128 | params = ((20, 20), ) 129 | 130 | class TestDiveAir20m30min(TestDiveAir, TMethodsMixin): 131 | """Test air 20m 30min.""" 132 | 133 | params = ((20, 30), ) 134 | 135 | class TestDiveAir20m40min(TestDiveAir, TMethodsMixin): 136 | """Test air 20m 40min.""" 137 | 138 | params = ((20, 40), ) 139 | 140 | class TestDiveAir20m50min(TestDiveAir, TMethodsMixin): 141 | """Test air 20m 50min.""" 142 | 143 | params = ((20, 50), ) 144 | 145 | 146 | # ==================================================== 30m tests ============== 147 | class TestDiveAir30m10min(TestDiveAir, TMethodsMixin): 148 | """Test air 30m 10min.""" 149 | 150 | params = ((30, 10), ) 151 | 152 | 153 | class TestDiveAir30m20min(TestDiveAir, TMethodsMixin): 154 | """Test air 30m 20min.""" 155 | 156 | params = ((30, 20), ) 157 | 158 | 159 | class TestDiveAir30m30min(TestDiveAir, TMethodsMixin): 160 | """Test air 30m 30min.""" 161 | 162 | params = ((30, 30), ) 163 | 164 | class TestDiveAir30m40min(TestDiveAir, TMethodsMixin): 165 | """Test air 30m 40min.""" 166 | 167 | params = ((30, 40), ) 168 | 169 | class TestDiveAir30m50min(TestDiveAir, TMethodsMixin): 170 | """Test air 30m 50min.""" 171 | 172 | params = ((30, 50), ) 173 | 174 | # ==================================================== 40m tests ============== 175 | class TestDiveAir40m10min(TestDiveAir, TMethodsMixin): 176 | """Test air 40m 10min.""" 177 | 178 | params = ((40, 10), ) 179 | 180 | class TestDiveAir40m20min(TestDiveAir, TMethodsMixin): 181 | """Test air 40m 20min.""" 182 | 183 | params = ((40, 20), ) 184 | 185 | 186 | class TestDiveAir40m30min(TestDiveAir, TMethodsMixin): 187 | """Test air 40m 30min.""" 188 | 189 | params = ((40, 30), ) 190 | 191 | class TestDiveAir40m40min(TestDiveAir, TMethodsMixin): 192 | """Test air 40m 40min.""" 193 | 194 | params = ((40, 40), ) 195 | 196 | class TestDiveAir40m50min(TestDiveAir, TMethodsMixin): 197 | """Test air 40m 50min.""" 198 | 199 | params = ((40, 50), ) 200 | 201 | # ==================================================== 50m tests ============== 202 | class TestDiveAir50m10min(TestDiveAir, TMethodsMixin): 203 | """Test air 50m 10min.""" 204 | 205 | params = ((50, 10), ) 206 | 207 | class TestDiveAir50m20min(TestDiveAir, TMethodsMixin): 208 | """Test air 50m 20min.""" 209 | 210 | params = ((50, 20), ) 211 | 212 | class TestDiveAir50m30min(TestDiveAir, TMethodsMixin): 213 | """Test air 50m 30min.""" 214 | 215 | params = ((50, 30), ) 216 | 217 | class TestDiveAir50m40min(TestDiveAir, TMethodsMixin): 218 | """Test air 50m 40min.""" 219 | 220 | params = ((50, 40), ) 221 | 222 | 223 | class TestDiveAir50m50min(TestDiveAir, TMethodsMixin): 224 | """Test air 50m 50min.""" 225 | 226 | params = ((50, 50), ) 227 | 228 | 229 | # ==================================================== 60m tests ============== 230 | class TestDiveAir60m10min(TestDiveAir, TMethodsMixin): 231 | """Test air 60m 10min.""" 232 | 233 | params = ((60, 10), ) 234 | 235 | 236 | class TestDiveAir60m20min(TestDiveAir, TMethodsMixin): 237 | """Test air 60m 20min.""" 238 | 239 | params = ((60, 20), ) 240 | 241 | 242 | class TestDiveAir60m25min(TestDiveAir, TMethodsMixin): 243 | """Test air 60m 25min.""" 244 | 245 | params = ((60, 25), ) 246 | 247 | class TestDiveAir60m30min(TestDiveAir, TMethodsMixin): 248 | """Test air 60m 30min.""" 249 | 250 | params = ((60, 30), ) 251 | 252 | 253 | # ==================================================== 70m tests ============== 254 | class TestDiveAir70m10min(TestDive): 255 | """Test air 70m 10min.""" 256 | 257 | params = ((70, 10), ) 258 | 259 | def runTest(self): 260 | """Run one test.""" 261 | try: 262 | diveseg1 = SegmentDive(self.params[0][0], self.params[0][1] * 60, 263 | self.airdouble, 0) 264 | self.profile1 = Dive([diveseg1], [self.airdouble]) 265 | self.profile1.do_dive() 266 | except UnauthorizedMod: 267 | pass 268 | else: 269 | self.fail("should raise UnauthorizedMod") 270 | 271 | 272 | # ======================= Multilevel Dive ===================================== 273 | class TestDiveAirMultilevel1(TestDiveAirMultilevel, TMethodsMixin): 274 | """Multilevel dive test.""" 275 | 276 | params = ((40, 10), (50, 12), (30, 15)) 277 | name = 'multilevel1' 278 | -------------------------------------------------------------------------------- /dipplanner/tools.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | """Tools module. 20 | 21 | This modules contains some utility functions like unit conversions, etc... 22 | """ 23 | import math 24 | import re 25 | 26 | # local imports 27 | from dipplanner import settings 28 | 29 | 30 | def safe_eval_calculator(text_to_eval): 31 | """Small an safe eval function usable only for simple calculation. 32 | 33 | (only the basic operators) 34 | 35 | :param float text_to_eval: the text (formula) that should be evaluated 36 | 37 | :returns: the result of the calculation. 38 | :rtype: float 39 | 40 | :raises ValueError: when given expression is not constitued of only 41 | numbers and operators 42 | :raises SyntaxError: when the given expression is incorrect 43 | 44 | """ 45 | expr = r"^([0-9]|\+|\*|\/|\-|\.|\ )+$" 46 | re_result = re.match(expr, text_to_eval) 47 | if re_result is None: 48 | raise ValueError("Only numbers and simple operators (*+-/) " 49 | "are allowed") 50 | else: 51 | return eval(re_result.group(0), {'__builtins__': None}, {}) 52 | 53 | 54 | def seconds_to_mmss(seconds): 55 | """Convert a value in seconds into a str representing time in min & sec. 56 | 57 | (like 2:06) 58 | It does returns only minutes and seconds, not hours, minutes and seconds 59 | 60 | :param float seconds: the duration in seconds 61 | 62 | :returns: the time in minutes and seconds. 63 | :rtype: str 64 | 65 | :raises ValueError: when bad time values 66 | 67 | ex:: 68 | " 23:45" 69 | "112:33" 70 | ... 71 | """ 72 | if seconds < 0: 73 | raise ValueError("time can not be negative") 74 | 75 | text = "%3d:%02d" % (int(seconds / 60), int(seconds % 60)) 76 | return text 77 | 78 | 79 | def seconds_to_hhmmss(seconds): 80 | """Convert a value in seconds into a str representing time in h, min & sec. 81 | 82 | like 22:34:44 83 | 84 | 85 | :param float seconds: the duration in seconds 86 | 87 | :returns: the time in hour - minutes and seconds 88 | :rtype: str 89 | 90 | :raises ValueError: when bad time values is given 91 | 92 | ex:: 93 | "00:23:45" 94 | "01:52:33" 95 | """ 96 | if seconds < 0: 97 | raise ValueError("time can not be negative") 98 | 99 | hours = seconds // (60 * 60) 100 | seconds %= (60 * 60) 101 | minutes = seconds // 60 102 | seconds %= 60 103 | return "%02i:%02i:%02i" % (hours, minutes, seconds) 104 | 105 | 106 | def altitude_or_depth_to_absolute_pressure(altitude_or_depth): 107 | """Output absolute pressure for give "depth" in meter. 108 | 109 | * If depth is positive it's considered altitude depth 110 | * If depth is negative it's considered depth in water 111 | 112 | :param float altitude_or_depth: in meter 113 | 114 | :returns: resulting absolute pressure in bar 115 | :rtype: float 116 | 117 | :raises ValueError: if altitude > 10000m 118 | """ 119 | if altitude_or_depth < 0: 120 | return (depth_to_pressure(-altitude_or_depth) + 121 | settings.AMBIANT_PRESSURE_SURFACE) 122 | else: 123 | return altitude_to_pressure(altitude_or_depth) 124 | 125 | 126 | def altitude_to_pressure(altitude): 127 | """Convert a given altitude in pressure in bar. 128 | 129 | uses the formula: 130 | p = 101325.(1-2.25577.10^-5.h)^5.25588 131 | 132 | :param float altitude: current altitude in meter 133 | 134 | :returns: resulting pressure in bar 135 | :rtype: float 136 | 137 | :raises ValueError: when bad altitude is given (bad or <0 or > 10000 m) 138 | """ 139 | if altitude < 0: 140 | raise ValueError("altitude can not be negative") 141 | if altitude > 10000: 142 | raise ValueError("altitude can not higher than 10000m") 143 | 144 | return (math.pow(1 - 2.25577 * math.pow(10, -5) * altitude, 5.25588) * 145 | settings.AMBIANT_PRESSURE_SEA_LEVEL) 146 | 147 | 148 | def depth_to_pressure(depth, method=None): 149 | """Calculate pressure based on given depth. 150 | 151 | depending of choosen mehod (complex or not), the calculation is done 152 | either /10 or using water density and g. 153 | 154 | :param float depth: in meter 155 | :param str method: 'complex' for complex method, any other string for 156 | simple method. 157 | 158 | :returns: depth pressure in bar 159 | :rtype: float 160 | """ 161 | if method is None: 162 | method = settings.METHOD_FOR_DEPTH_CALCULATION 163 | if method == 'complex': 164 | g = 9.81 165 | return settings.WATER_DENSITY * 1E3 * g * float(depth) * 1E-5 166 | else: 167 | return depth / 10 168 | 169 | 170 | def pressure_to_depth(pressure, method=None): 171 | """Calculate depth based on given pressure. 172 | 173 | depending of choosen mehod (complex or not), the calculation is done 174 | either * 10 or using water density and g. 175 | 176 | :param float pressure: pressure in bar 177 | :param str method: 'complex' for complex method, any other string for 178 | simple method. 179 | 180 | :returns: depth in meter 181 | :rtype: float 182 | """ 183 | if method is None: 184 | method = settings.METHOD_FOR_DEPTH_CALCULATION 185 | if method == 'complex': 186 | g = 9.81 187 | return pressure / (settings.WATER_DENSITY * 1E3 * g * 1E-5) 188 | else: 189 | return pressure * 10 190 | 191 | 192 | def calculate_pp_h2o_surf(temperature=20): 193 | """Calculate and return vapor pressure of water at surface. 194 | 195 | using Antoine equation 196 | (http://en.wikipedia.org/wiki/Vapour_pressure_of_water) 197 | 198 | :param float temperature: [OPTIONNAL] in ° Celcius 199 | 200 | :returns: ppH2O in bar 201 | :rtype: float 202 | 203 | :raises ValueError: when temperature exceed maximum value for calculation 204 | (>=374 °C) 205 | """ 206 | mm_hg_to_bar = 1.0 / 750.0615 207 | 208 | if temperature < 1: 209 | temperature = 1 210 | if temperature >= 1 and temperature <= 100: 211 | const_a = 8.07131 212 | const_b = 1730.63 213 | const_c = 233.426 214 | elif temperature > 100 and temperature < 374: 215 | const_a = 8.14019 216 | const_b = 1810.94 217 | const_c = 244.485 218 | else: 219 | raise ValueError("Temperature is too High") 220 | 221 | pressure_mm_hg = 10 ** (const_a - (const_b / (const_c + 222 | float(temperature)))) 223 | return pressure_mm_hg * mm_hg_to_bar 224 | 225 | 226 | def convert_bar_to_psi(value): 227 | """SI --> imperial pressure conversion function. 228 | 229 | :param float value: pressure in bar 230 | 231 | :returns: pressure in psi 232 | :rtype: float 233 | """ 234 | return value * 14.5037744 235 | 236 | 237 | def convert_psi_to_bar(value): 238 | """Imperial --> SI pressure conversion function. 239 | 240 | :param float value: pressure in psi 241 | 242 | :returns: pressure in bar 243 | :rtype: float 244 | """ 245 | return value / 14.5037744 246 | 247 | 248 | def convert_liter_to_cubicfeet(value): 249 | """SI --> imperial volume conversion function. 250 | 251 | :param float value: volume in liter 252 | 253 | :returns: volume in cubicfeet 254 | :rtype: float 255 | """ 256 | return value / (math.pow(0.3048, 3) * 1000) 257 | 258 | 259 | def convert_cubicfeet_to_liter(value): 260 | """Imperial --> SI volume conversion function. 261 | 262 | :param float value: volume in cubicfeet 263 | 264 | :returns: volume in liter 265 | :rtype: float 266 | """ 267 | return value * (math.pow(0.3048, 3) * 1000) 268 | 269 | 270 | def convert_meter_to_feet(value): 271 | """SI --> imperial distance conversion function. 272 | 273 | :param float value: length in meter 274 | 275 | :returns: length in feet 276 | :rtype: float 277 | """ 278 | return value / 0.3048 279 | 280 | 281 | def convert_feet_to_meter(value): 282 | """Imperial --> SI distance conversion function. 283 | 284 | :param float value: length in feet 285 | 286 | :returns: length in meter 287 | :rtype: float 288 | """ 289 | return value * 0.3048 290 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # dipplanner documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Aug 15 00:20:57 2012. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | import sys 17 | sys.path.append("../") 18 | from build_scripts.version import get_git_version 19 | 20 | # If extensions (or modules to document with autodoc) are in another directory, 21 | # add these directories to sys.path here. If the directory is relative to the 22 | # documentation root, use os.path.abspath to make it absolute, like shown here. 23 | #sys.path.insert(0, os.path.abspath('.')) 24 | 25 | # -- General configuration ----------------------------------------------------- 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | #needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be extensions 31 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 32 | extensions = ['sphinx.ext.autodoc', 33 | 'sphinx.ext.todo', 34 | 'sphinx.ext.viewcode', 35 | 'sphinx.ext.autosummary', ] 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # The suffix of source filenames. 41 | source_suffix = '.rst' 42 | 43 | # The encoding of source files. 44 | #source_encoding = 'utf-8-sig' 45 | 46 | # include TODOs 47 | todo_include_todos = True 48 | 49 | # The master toctree document. 50 | master_doc = 'index' 51 | 52 | # General information about the project. 53 | project = u'dipplanner' 54 | copyright = u'2011-2012, Thomas Chiroux' 55 | 56 | # The version info for the project you're documenting, acts as replacement for 57 | # |version| and |release|, also used in various other places throughout the 58 | # built documents. 59 | # 60 | # The short X.Y version. 61 | version = get_git_version() 62 | # The full version, including alpha/beta/rc tags. 63 | release = version 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | #language = None 68 | 69 | # There are two options for replacing |today|: either, you set today to some 70 | # non-false value, then it is used: 71 | #today = '' 72 | # Else, today_fmt is used as the format for a strftime call. 73 | #today_fmt = '%B %d, %Y' 74 | 75 | # List of patterns, relative to source directory, that match files and 76 | # directories to ignore when looking for source files. 77 | exclude_patterns = ['_build'] 78 | 79 | # The reST default role (used for this markup: `text`) to use for all documents. 80 | #default_role = None 81 | 82 | # If true, '()' will be appended to :func: etc. cross-reference text. 83 | add_function_parentheses = True 84 | 85 | # If true, the current module name will be prepended to all description 86 | # unit titles (such as .. function::). 87 | add_module_names = True 88 | 89 | # If true, sectionauthor and moduleauthor directives will be shown in the 90 | # output. They are ignored by default. 91 | #show_authors = False 92 | 93 | # The name of the Pygments (syntax highlighting) style to use. 94 | pygments_style = 'sphinx' 95 | 96 | # A list of ignored prefixes for module index sorting. 97 | #modindex_common_prefix = [] 98 | 99 | 100 | # -- Options for HTML output --------------------------------------------------- 101 | 102 | # The theme to use for HTML and HTML Help pages. See the documentation for 103 | # a list of builtin themes. 104 | html_theme = 'default' 105 | 106 | # Theme options are theme-specific and customize the look and feel of a theme 107 | # further. For a list of options available for each theme, see the 108 | # documentation. 109 | #html_theme_options = {} 110 | 111 | # Add any paths that contain custom themes here, relative to this directory. 112 | #html_theme_path = [] 113 | 114 | # The name for this set of Sphinx documents. If None, it defaults to 115 | # " v documentation". 116 | #html_title = None 117 | 118 | # A shorter title for the navigation bar. Default is the same as html_title. 119 | #html_short_title = None 120 | 121 | # The name of an image file (relative to this directory) to place at the top 122 | # of the sidebar. 123 | #html_logo = None 124 | 125 | # The name of an image file (within the static path) to use as favicon of the 126 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 127 | # pixels large. 128 | #html_favicon = None 129 | 130 | # Add any paths that contain custom static files (such as style sheets) here, 131 | # relative to this directory. They are copied after the builtin static files, 132 | # so a file named "default.css" will overwrite the builtin "default.css". 133 | html_static_path = ['_static'] 134 | 135 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 136 | # using the given strftime format. 137 | #html_last_updated_fmt = '%b %d, %Y' 138 | 139 | # If true, SmartyPants will be used to convert quotes and dashes to 140 | # typographically correct entities. 141 | #html_use_smartypants = True 142 | 143 | # Custom sidebar templates, maps document names to template names. 144 | #html_sidebars = {} 145 | 146 | # Additional templates that should be rendered to pages, maps page names to 147 | # template names. 148 | #html_additional_pages = {} 149 | 150 | # If false, no module index is generated. 151 | #html_domain_indices = True 152 | 153 | # If false, no index is generated. 154 | #html_use_index = True 155 | 156 | # If true, the index is split into individual pages for each letter. 157 | #html_split_index = False 158 | 159 | # If true, links to the reST sources are added to the pages. 160 | #html_show_sourcelink = True 161 | 162 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 163 | #html_show_sphinx = True 164 | 165 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 166 | #html_show_copyright = True 167 | 168 | # If true, an OpenSearch description file will be output, and all pages will 169 | # contain a tag referring to it. The value of this option must be the 170 | # base URL from which the finished HTML is served. 171 | #html_use_opensearch = '' 172 | 173 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 174 | #html_file_suffix = None 175 | 176 | # Output file base name for HTML help builder. 177 | htmlhelp_basename = 'dipplannerdoc' 178 | 179 | 180 | # -- Options for LaTeX output -------------------------------------------------- 181 | 182 | latex_elements = { 183 | # The paper size ('letterpaper' or 'a4paper'). 184 | #'papersize': 'letterpaper', 185 | 186 | # The font size ('10pt', '11pt' or '12pt'). 187 | #'pointsize': '10pt', 188 | 189 | # Additional stuff for the LaTeX preamble. 190 | #'preamble': '', 191 | } 192 | 193 | # Grouping the document tree into LaTeX files. List of tuples 194 | # (source start file, target name, title, author, documentclass [howto/manual]). 195 | latex_documents = [ 196 | ('index', 'dipplanner.tex', u'dipplanner Documentation', 197 | u'Thomas Chiroux', 'manual'), 198 | ] 199 | 200 | # The name of an image file (relative to this directory) to place at the top of 201 | # the title page. 202 | #latex_logo = None 203 | 204 | # For "manual" documents, if this is true, then toplevel headings are parts, 205 | # not chapters. 206 | #latex_use_parts = False 207 | 208 | # If true, show page references after internal links. 209 | #latex_show_pagerefs = False 210 | 211 | # If true, show URL addresses after external links. 212 | #latex_show_urls = False 213 | 214 | # Documents to append as an appendix to all manuals. 215 | #latex_appendices = [] 216 | 217 | # If false, no module index is generated. 218 | #latex_domain_indices = True 219 | 220 | 221 | # -- Options for manual page output -------------------------------------------- 222 | 223 | # One entry per manual page. List of tuples 224 | # (source start file, name, description, authors, manual section). 225 | man_pages = [ 226 | ('index', 'dipplanner', u'dipplanner Documentation', 227 | [u'Thomas Chiroux'], 1) 228 | ] 229 | 230 | # If true, show URL addresses after external links. 231 | #man_show_urls = False 232 | 233 | 234 | # -- Options for Texinfo output ------------------------------------------------ 235 | 236 | # Grouping the document tree into Texinfo files. List of tuples 237 | # (source start file, target name, title, author, 238 | # dir menu entry, description, category) 239 | texinfo_documents = [ 240 | ('index', 'dipplanner', u'dipplanner Documentation', 241 | u'Thomas Chiroux', 'dipplanner', 'One line description of project.', 242 | 'Miscellaneous'), 243 | ] 244 | 245 | # Documents to append as an appendix to all manuals. 246 | #texinfo_appendices = [] 247 | 248 | # If false, no module index is generated. 249 | #texinfo_domain_indices = True 250 | 251 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 252 | #texinfo_show_urls = 'footnote' 253 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_airdeco_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test Dives with air and deco.""" 23 | import json 24 | import pkg_resources 25 | 26 | 27 | from dipplanner.dive import Dive 28 | from dipplanner.segment import SegmentDive 29 | from dipplanner.segment import UnauthorizedMod 30 | from dipplanner.tools import seconds_to_mmss 31 | 32 | from dipplanner.tests.common import TestDive, TMethodsMixinDeco 33 | 34 | 35 | class TestDiveAirDecoNx80Base(TestDive): 36 | """Class for test air dive.""" 37 | 38 | def setUp(self): 39 | """Init of the tests.""" 40 | super().setUp() 41 | 42 | # load json results file 43 | self.results = json.loads( 44 | pkg_resources.resource_string( 45 | "dipplanner.tests", 46 | __name__.split('.')[-1] + '.json').decode('utf-8')) 47 | 48 | @property 49 | def details(self): 50 | """print detailed results.""" 51 | return '"%s": ["%s", %f, %f, %d, %d, %f, %s, %f, %s], ' % ( 52 | self.name, 53 | seconds_to_mmss(self.profile1.run_time), 54 | self.profile1.model.ox_tox.otu, 55 | self.profile1.model.ox_tox.cns * 100, 56 | self.profile1.no_flight_time(), 57 | self.profile1.full_desat_time(), 58 | self.profile1.tanks[0].used_gas, 59 | str(self.profile1.tanks[0].check_rule()).lower(), 60 | self.profile1.tanks[1].used_gas, 61 | str(self.profile1.tanks[1].check_rule()).lower()) 62 | 63 | 64 | class TestDiveAirDecoNx80(TestDiveAirDecoNx80Base): 65 | """Class for test air dive.""" 66 | 67 | def setUp(self): 68 | """Init of the tests.""" 69 | super().setUp() 70 | 71 | self.dive_tank = self.airtank12 72 | self.all_tanks = [self.airtank12, self.deco1] 73 | self.do_dive() 74 | 75 | 76 | class TestDiveMultilevel(TestDiveAirDecoNx80Base): 77 | """Multilevel dive test.""" 78 | 79 | def setUp(self): 80 | """Init of multilevel test.""" 81 | super().setUp() 82 | self.dive_tank = self.airdouble 83 | self.all_tanks = [self.airdouble, self.deco1] 84 | self.do_dive() 85 | 86 | 87 | # AIR + DECO Nx80 ============================================================= 88 | # ==================================================== 10m tests ============== 89 | class TestDiveAirDecoNx8010m10min(TestDiveAirDecoNx80, TMethodsMixinDeco): 90 | """Test air 10m 10min.""" 91 | 92 | params = ((10, 10), ) 93 | 94 | class TestDiveAirDecoNx8010m20min(TestDiveAirDecoNx80, TMethodsMixinDeco): 95 | """Test air 10m 20min.""" 96 | 97 | params = ((10, 20), ) 98 | 99 | class TestDiveAirDecoNx8010m30min(TestDiveAirDecoNx80, TMethodsMixinDeco): 100 | """Test air 10m 30min.""" 101 | 102 | params = ((10, 30), ) 103 | 104 | class TestDiveAirDecoNx8010m40min(TestDiveAirDecoNx80, TMethodsMixinDeco): 105 | """Test air 10m 40min.""" 106 | 107 | params = ((10, 40), ) 108 | 109 | class TestDiveAirDecoNx8010m50min(TestDiveAirDecoNx80, TMethodsMixinDeco): 110 | """Test air 10m 50min.""" 111 | 112 | params = ((10, 50), ) 113 | 114 | class TestDiveAirDecoNx8010m60min(TestDiveAirDecoNx80, TMethodsMixinDeco): 115 | """Test air 10m 60min.""" 116 | 117 | params = ((10, 60), ) 118 | 119 | 120 | class TestDiveAirDecoNx8010m70min(TestDiveAirDecoNx80, TMethodsMixinDeco): 121 | """Test air 10m 70min.""" 122 | 123 | params = ((10, 70), ) 124 | 125 | 126 | # ==================================================== 20m tests ============== 127 | class TestDiveAirDecoNx8020m10min(TestDiveAirDecoNx80, TMethodsMixinDeco): 128 | """Test air 20m 10min.""" 129 | 130 | params = ((20, 10), ) 131 | 132 | 133 | class TestDiveAirDecoNx8020m20min(TestDiveAirDecoNx80, TMethodsMixinDeco): 134 | """Test air 20m 20min.""" 135 | 136 | params = ((20, 20), ) 137 | 138 | 139 | class TestDiveAirDecoNx8020m30min(TestDiveAirDecoNx80, TMethodsMixinDeco): 140 | """Test air 20m 30min.""" 141 | 142 | params = ((20, 30), ) 143 | 144 | 145 | class TestDiveAirDecoNx8020m40min(TestDiveAirDecoNx80, TMethodsMixinDeco): 146 | """Test air 20m 40min.""" 147 | 148 | params = ((20, 40), ) 149 | 150 | 151 | class TestDiveAirDecoNx8020m50min(TestDiveAirDecoNx80, TMethodsMixinDeco): 152 | """Test air 20m 50min.""" 153 | 154 | params = ((20, 50), ) 155 | 156 | 157 | # ==================================================== 30m tests ============== 158 | class TestDiveAirDecoNx8030m10min(TestDiveAirDecoNx80, TMethodsMixinDeco): 159 | """Test air 30m 10min.""" 160 | 161 | params = ((30, 10), ) 162 | 163 | 164 | class TestDiveAirDecoNx8030m20min(TestDiveAirDecoNx80, TMethodsMixinDeco): 165 | """Test air 30m 20min.""" 166 | 167 | params = ((30, 20), ) 168 | 169 | 170 | class TestDiveAirDecoNx8030m30min(TestDiveAirDecoNx80, TMethodsMixinDeco): 171 | """Test air 30m 30min.""" 172 | 173 | params = ((30, 30), ) 174 | 175 | 176 | class TestDiveAirDecoNx8030m40min(TestDiveAirDecoNx80, TMethodsMixinDeco): 177 | """Test air 30m 40min.""" 178 | 179 | params = ((30, 40), ) 180 | 181 | class TestDiveAirDecoNx8030m50min(TestDiveAirDecoNx80, TMethodsMixinDeco): 182 | """Test air 30m 50min.""" 183 | 184 | params = ((30, 50), ) 185 | 186 | 187 | # ==================================================== 40m tests ============== 188 | class TestDiveAirDecoNx8040m10min(TestDiveAirDecoNx80, TMethodsMixinDeco): 189 | """Test air 40m 10min.""" 190 | 191 | params = ((40, 10), ) 192 | 193 | 194 | class TestDiveAirDecoNx8040m20min(TestDiveAirDecoNx80, TMethodsMixinDeco): 195 | """Test air 40m 20min.""" 196 | 197 | params = ((40, 20), ) 198 | 199 | 200 | class TestDiveAirDecoNx8040m30min(TestDiveAirDecoNx80, TMethodsMixinDeco): 201 | """Test air 40m 30min.""" 202 | 203 | params = ((40, 30), ) 204 | 205 | 206 | class TestDiveAirDecoNx8040m40min(TestDiveAirDecoNx80, TMethodsMixinDeco): 207 | """Test air 40m 40min.""" 208 | 209 | params = ((40, 40), ) 210 | 211 | 212 | class TestDiveAirDecoNx8040m50min(TestDiveAirDecoNx80, TMethodsMixinDeco): 213 | """Test air 40m 50min.""" 214 | 215 | params = ((40, 50), ) 216 | 217 | 218 | # ==================================================== 50m tests ============== 219 | class TestDiveAirDecoNx8050m10min(TestDiveAirDecoNx80, TMethodsMixinDeco): 220 | """Test air 50m 10min.""" 221 | 222 | params = ((50, 10), ) 223 | 224 | 225 | class TestDiveAirDecoNx8050m20min(TestDiveAirDecoNx80, TMethodsMixinDeco): 226 | """Test air 50m 20min.""" 227 | 228 | params = ((50, 20), ) 229 | 230 | 231 | class TestDiveAirDecoNx8050m30min(TestDiveAirDecoNx80, TMethodsMixinDeco): 232 | """Test air 50m 30min.""" 233 | 234 | params = ((50, 30), ) 235 | 236 | 237 | class TestDiveAirDecoNx8050m40min(TestDiveAirDecoNx80, TMethodsMixinDeco): 238 | """Test air 50m 40min.""" 239 | 240 | params = ((50, 40), ) 241 | 242 | 243 | class TestDiveAirDecoNx8050m50min(TestDiveAirDecoNx80, TMethodsMixinDeco): 244 | """Test air 50m 50min.""" 245 | 246 | params = ((50, 50), ) 247 | 248 | 249 | # ==================================================== 60m tests ============== 250 | class TestDiveAirDecoNx8060m10min(TestDiveAirDecoNx80, TMethodsMixinDeco): 251 | """Test air 60m 10min.""" 252 | 253 | params = ((60, 10), ) 254 | 255 | 256 | class TestDiveAirDecoNx8060m20min(TestDiveAirDecoNx80, TMethodsMixinDeco): 257 | """Test air 60m 20min.""" 258 | 259 | params = ((60, 20), ) 260 | 261 | class TestDiveAirDecoNx8060m25min(TestDiveAirDecoNx80, TMethodsMixinDeco): 262 | """Test air 60m 25min.""" 263 | 264 | params = ((60, 25), ) 265 | 266 | 267 | class TestDiveAirDecoNx8060m30min(TestDiveAirDecoNx80, TMethodsMixinDeco): 268 | """Test air 60m 30min.""" 269 | 270 | params = ((60, 30), ) 271 | 272 | 273 | # ==================================================== 70m tests ============== 274 | class TestDiveAirDecoNx8070m10min(TestDive): 275 | """Test air 70m 10min.""" 276 | 277 | params = ((70, 10), ) 278 | 279 | def runTest(self): 280 | """Run one test.""" 281 | try: 282 | diveseg1 = SegmentDive(self.params[0][0], self.params[0][1] * 60, 283 | self.airdouble, 0) 284 | self.profile1 = Dive([diveseg1], [self.airdouble]) 285 | self.profile1.do_dive() 286 | except UnauthorizedMod: 287 | pass 288 | else: 289 | self.fail("should raise UnauthorizedMod") 290 | 291 | 292 | # ======================= Multilevel Dive ===================================== 293 | class TestDiveMultilevel1(TestDiveMultilevel, TMethodsMixinDeco): 294 | """Multilevel dive test.""" 295 | 296 | params = ((40, 10), (50, 12), (30, 15)) 297 | name = 'multilevel1' 298 | -------------------------------------------------------------------------------- /dipplanner/tests/dive_txnormo_test.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2011-2016 Thomas Chiroux 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as 6 | # published by the Free Software Foundation, either version 3 of the 7 | # License, or (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. 16 | # If not, see 17 | # 18 | # This module is part of dipplanner, a Dive planning Tool written in python 19 | # pylint: disable=too-many-public-methods, protected-access, no-self-use 20 | # pylint: disable=too-few-public-methods, duplicate-code, invalid-name 21 | # pylint: disable=too-many-ancestors, attribute-defined-outside-init 22 | """Test Dives with air and deco.""" 23 | import json 24 | import pkg_resources 25 | 26 | from dipplanner.dive import Dive 27 | from dipplanner.segment import SegmentDive 28 | from dipplanner.segment import UnauthorizedMod 29 | from dipplanner.tools import seconds_to_mmss 30 | 31 | from dipplanner.tests.common import TestDive, TMethodsMixinDeco 32 | 33 | class TestDiveTxNormoDecoNx80Base(TestDive): 34 | """Class for test air dive.""" 35 | 36 | def setUp(self): 37 | """Init of the tests.""" 38 | super().setUp() 39 | 40 | # load json results file 41 | self.results = json.loads( 42 | pkg_resources.resource_string( 43 | "dipplanner.tests", 44 | __name__.split('.')[-1] + '.json').decode('utf-8')) 45 | 46 | @property 47 | def details(self): 48 | """print detailed results.""" 49 | return '"%s": ["%s", %f, %f, %d, %d, %f, %s, %f, %s], ' % ( 50 | self.name, 51 | seconds_to_mmss(self.profile1.run_time), 52 | self.profile1.model.ox_tox.otu, 53 | self.profile1.model.ox_tox.cns * 100, 54 | self.profile1.no_flight_time(), 55 | self.profile1.full_desat_time(), 56 | self.profile1.tanks[0].used_gas, 57 | str(self.profile1.tanks[0].check_rule()).lower(), 58 | self.profile1.tanks[1].used_gas, 59 | str(self.profile1.tanks[1].check_rule()).lower()) 60 | 61 | 62 | class TestDiveTxNormoDecoNx80(TestDiveTxNormoDecoNx80Base): 63 | """Class for test air dive.""" 64 | 65 | def setUp(self): 66 | """Init of the tests.""" 67 | super().setUp() 68 | 69 | self.dive_tank = self.txtanknormodbl 70 | self.all_tanks = [self.txtanknormodbl, self.deco1] 71 | self.do_dive() 72 | 73 | class TestDiveMultilevel(TestDiveTxNormoDecoNx80Base): 74 | """Multilevel dive test.""" 75 | 76 | def setUp(self): 77 | """Init of multilevel test.""" 78 | super().setUp() 79 | self.dive_tank = self.txtanknormodbl 80 | self.all_tanks = [self.txtanknormodbl, self.deco1] 81 | self.do_dive() 82 | 83 | 84 | # Trimix Normp + DECO Nx80 ==================================================== 85 | # ==================================================== 10m tests ============== 86 | class TestDiveTxNormoDecoNx8010m10min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 87 | """Test txnormo 10m 10min.""" 88 | 89 | params = ((10, 10), ) 90 | 91 | 92 | class TestDiveTxNormoDecoNx8010m20min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 93 | """Test txnormo 10m 20min.""" 94 | 95 | params = ((10, 20), ) 96 | 97 | 98 | class TestDiveTxNormoDecoNx8010m30min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 99 | """Test txnormo 10m 30min.""" 100 | 101 | params = ((10, 30), ) 102 | 103 | 104 | class TestDiveTxNormoDecoNx8010m40min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 105 | """Test txnormo 10m 40min.""" 106 | 107 | params = ((10, 40), ) 108 | 109 | 110 | class TestDiveTxNormoDecoNx8010m50min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 111 | """Test txnormo 10m 50min.""" 112 | 113 | params = ((10, 50), ) 114 | 115 | 116 | class TestDiveTxNormoDecoNx8010m60min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 117 | """Test txnormo 10m 60min.""" 118 | 119 | params = ((10, 60), ) 120 | 121 | 122 | class TestDiveTxNormoDecoNx8010m70min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 123 | """Test txnormo 10m 70min.""" 124 | 125 | params = ((10, 70), ) 126 | 127 | 128 | # ==================================================== 20m tests ============== 129 | class TestDiveTxNormoDecoNx8020m10min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 130 | """Test txnormo 20m 10min.""" 131 | 132 | params = ((20, 10), ) 133 | 134 | 135 | class TestDiveTxNormoDecoNx8020m20min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 136 | """Test txnormo 20m 20min.""" 137 | 138 | params = ((20, 20), ) 139 | 140 | 141 | class TestDiveTxNormoDecoNx8020m30min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 142 | """Test txnormo 20m 30min.""" 143 | 144 | params = ((20, 30), ) 145 | 146 | 147 | class TestDiveTxNormoDecoNx8020m40min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 148 | """Test txnormo 20m 40min.""" 149 | 150 | params = ((20, 40), ) 151 | 152 | 153 | class TestDiveTxNormoDecoNx8020m50min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 154 | """Test txnormo 20m 50min.""" 155 | 156 | params = ((20, 50), ) 157 | 158 | 159 | # ==================================================== 30m tests ============== 160 | class TestDiveTxNormoDecoNx8030m10min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 161 | """Test txnormo 30m 10min.""" 162 | 163 | params = ((30, 10), ) 164 | 165 | 166 | class TestDiveTxNormoDecoNx8030m20min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 167 | """Test txnormo 30m 20min.""" 168 | 169 | params = ((30, 20), ) 170 | 171 | 172 | class TestDiveTxNormoDecoNx8030m30min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 173 | """Test txnormo 30m 30min.""" 174 | 175 | params = ((30, 30), ) 176 | 177 | class TestDiveTxNormoDecoNx8030m40min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 178 | """Test txnormo 30m 40min.""" 179 | 180 | params = ((30, 40), ) 181 | 182 | class TestDiveTxNormoDecoNx8030m50min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 183 | """Test txnormo 30m 50min.""" 184 | 185 | params = ((30, 50), ) 186 | 187 | # ==================================================== 40m tests ============== 188 | class TestDiveTxNormoDecoNx8040m10min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 189 | """Test txnormo 40m 10min.""" 190 | 191 | params = ((40, 10), ) 192 | 193 | 194 | class TestDiveTxNormoDecoNx8040m20min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 195 | """Test txnormo 40m 20min.""" 196 | 197 | params = ((40, 20), ) 198 | 199 | class TestDiveTxNormoDecoNx8040m30min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 200 | """Test txnormo 40m 30min.""" 201 | 202 | params = ((40, 30), ) 203 | 204 | class TestDiveTxNormoDecoNx8040m40min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 205 | """Test txnormo 40m 40min.""" 206 | 207 | params = ((40, 40), ) 208 | 209 | class TestDiveTxNormoDecoNx8040m50min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 210 | """Test txnormo 40m 50min.""" 211 | 212 | params = ((40, 50), ) 213 | 214 | # ==================================================== 50m tests ============== 215 | class TestDiveTxNormoDecoNx8050m10min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 216 | """Test txnormo 50m 10min.""" 217 | 218 | params = ((50, 10), ) 219 | 220 | class TestDiveTxNormoDecoNx8050m20min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 221 | """Test txnormo 50m 20min.""" 222 | 223 | params = ((50, 20), ) 224 | 225 | class TestDiveTxNormoDecoNx8050m30min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 226 | """Test txnormo 50m 30min.""" 227 | 228 | params = ((50, 30), ) 229 | 230 | 231 | class TestDiveTxNormoDecoNx8050m40min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 232 | """Test txnormo 50m 40min.""" 233 | 234 | params = ((50, 40), ) 235 | 236 | 237 | class TestDiveTxNormoDecoNx8050m50min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 238 | """Test txnormo 50m 50min.""" 239 | 240 | params = ((50, 50), ) 241 | 242 | 243 | # ==================================================== 60m tests ============== 244 | class TestDiveTxNormoDecoNx8060m10min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 245 | """Test txnormo 60m 10min.""" 246 | 247 | params = ((60, 10), ) 248 | 249 | 250 | class TestDiveTxNormoDecoNx8060m20min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 251 | """Test txnormo 60m 20min.""" 252 | 253 | params = ((60, 20), ) 254 | 255 | class TestDiveTxNormoDecoNx8060m25min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 256 | """Test txnormo 60m 25min.""" 257 | 258 | params = ((60, 25), ) 259 | 260 | class TestDiveTxNormoDecoNx8060m30min(TestDiveTxNormoDecoNx80, TMethodsMixinDeco): 261 | """Test txnormo 60m 30min.""" 262 | 263 | params = ((60, 30), ) 264 | 265 | # ==================================================== 70m tests ============== 266 | class TestDiveTxNormoDecoNx8070m10min(TestDive): 267 | """Test txnormo 70m 10min.""" 268 | 269 | params = ((70, 10), ) 270 | 271 | def runTest(self): 272 | """Run one test.""" 273 | try: 274 | diveseg1 = SegmentDive(self.params[0][0], self.params[0][1] * 60, 275 | self.txtanknormodbl, 0) 276 | self.profile1 = Dive([diveseg1], [self.txtanknormodbl, 277 | self.deco1]) 278 | self.profile1.do_dive() 279 | except UnauthorizedMod: 280 | pass 281 | else: 282 | self.fail("should raise UnauthorizedMod") 283 | 284 | 285 | # ======================= Multilevel Dive ===================================== 286 | class TestDiveMultilevel1(TestDiveMultilevel, TMethodsMixinDeco): 287 | """Multilevel dive test.""" 288 | 289 | params = ((40, 10), (50, 12), (30, 15)) 290 | name = 'multilevel1' 291 | --------------------------------------------------------------------------------