├── .gitignore ├── gfs └── .gitignore ├── predict ├── .gitignore ├── icons │ ├── arrow.png │ ├── balloon.png │ ├── target-1.png │ ├── target-2.png │ ├── target-3.png │ ├── target-4.png │ ├── target-5.png │ ├── target-6.png │ ├── target-7.png │ ├── target-8.png │ ├── target-9.png │ ├── target-10.png │ ├── target-11.png │ ├── target-12.png │ ├── target-13.png │ ├── target-14.png │ ├── target-15.png │ ├── target-16.png │ ├── target-17.png │ ├── target-18.png │ ├── target-red.png │ ├── target-blue.png │ └── target-yellow.png ├── images │ ├── csv.gif │ ├── kml.png │ ├── arrow.png │ ├── balloon.png │ ├── tipsy.gif │ ├── target-1.png │ ├── target-10.png │ ├── target-11.png │ ├── target-12.png │ ├── target-13.png │ ├── target-14.png │ ├── target-15.png │ ├── target-16.png │ ├── target-17.png │ ├── target-18.png │ ├── target-2.png │ ├── target-3.png │ ├── target-4.png │ ├── target-5.png │ ├── target-6.png │ ├── target-7.png │ ├── target-8.png │ ├── target-9.png │ ├── balloon-sm.png │ ├── drag_handle.png │ ├── pop-marker.png │ ├── target-1-sm.png │ ├── target-8-sm.png │ ├── target-blue.png │ ├── target-red.png │ ├── marker-sm-red.png │ ├── target-yellow.png │ └── marker-sm-black.png ├── css │ ├── cupertino │ │ └── images │ │ │ ├── ui-icons_2694e8_256x240.png │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ ├── ui-icons_3d80b3_256x240.png │ │ │ ├── ui-icons_72a7cf_256x240.png │ │ │ ├── ui-icons_ffffff_256x240.png │ │ │ ├── ui-bg_flat_15_cd0a0a_40x100.png │ │ │ ├── ui-bg_glass_100_e4f1fb_1x400.png │ │ │ ├── ui-bg_glass_50_3baae3_1x400.png │ │ │ ├── ui-bg_glass_80_d7ebf9_1x400.png │ │ │ ├── ui-bg_diagonals-thick_90_eeeeee_40x40.png │ │ │ ├── ui-bg_highlight-hard_100_f2f5f7_1x100.png │ │ │ ├── ui-bg_highlight-hard_70_000000_1x100.png │ │ │ ├── ui-bg_highlight-soft_100_deedf7_1x100.png │ │ │ └── ui-bg_highlight-soft_25_ffef8f_1x100.png │ ├── tipsy.css │ ├── calc.css │ └── pred.css ├── includes │ ├── config.inc.php │ ├── statsd.php │ └── functions.inc.php ├── js │ ├── pred │ │ ├── pred-config.js │ │ ├── pred-ui.js │ │ ├── pred-cookie.js │ │ ├── pred-event.js │ │ └── pred-map.js │ ├── jquery │ │ └── jquery.tipsy.js │ ├── utils │ │ └── date.jsport.js │ └── calc │ │ └── calc.js ├── sites.json ├── admin.php ├── ajax.php └── kml.php ├── pred_src ├── .gitignore ├── ini │ ├── README │ ├── README.orig │ ├── LICENSE │ ├── dictionary.h │ ├── iniparser.h │ └── dictionary.c ├── CMakeLists.txt ├── pred.h ├── util │ ├── random.h │ ├── getline.c │ ├── getline.h │ ├── getdelim.h │ ├── random.c │ ├── gopt.h │ ├── getdelim.c │ └── gopt.c ├── cmake_install.cmake ├── wind │ ├── wind_file.h │ ├── wind_file_cache.h │ └── wind_file_cache.c ├── run_model.h ├── altitude.h ├── altitude.c ├── run_model.c └── pred.c ├── AUTHORS ├── requirements.txt ├── cron ├── clear-pydap-cache-cronjob.sh ├── prune-predictions-cronjob.sh └── crontab-example ├── CREDITS └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /gfs/.gitignore: -------------------------------------------------------------------------------- 1 | tmp* 2 | -------------------------------------------------------------------------------- /predict/.gitignore: -------------------------------------------------------------------------------- 1 | preds/* 2 | -------------------------------------------------------------------------------- /pred_src/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | Makefile 3 | pred 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Adam Greig 2 | Jon Sowman 3 | 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Pydap==3.0.1 2 | numpy==1.5.1 3 | statsd-client==1.0.2 4 | simplejson==2.1.1 5 | 6 | -------------------------------------------------------------------------------- /predict/icons/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/arrow.png -------------------------------------------------------------------------------- /predict/images/csv.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/csv.gif -------------------------------------------------------------------------------- /predict/images/kml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/kml.png -------------------------------------------------------------------------------- /predict/icons/balloon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/balloon.png -------------------------------------------------------------------------------- /predict/icons/target-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-1.png -------------------------------------------------------------------------------- /predict/icons/target-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-2.png -------------------------------------------------------------------------------- /predict/icons/target-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-3.png -------------------------------------------------------------------------------- /predict/icons/target-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-4.png -------------------------------------------------------------------------------- /predict/icons/target-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-5.png -------------------------------------------------------------------------------- /predict/icons/target-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-6.png -------------------------------------------------------------------------------- /predict/icons/target-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-7.png -------------------------------------------------------------------------------- /predict/icons/target-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-8.png -------------------------------------------------------------------------------- /predict/icons/target-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-9.png -------------------------------------------------------------------------------- /predict/images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/arrow.png -------------------------------------------------------------------------------- /predict/images/balloon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/balloon.png -------------------------------------------------------------------------------- /predict/images/tipsy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/tipsy.gif -------------------------------------------------------------------------------- /predict/icons/target-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-10.png -------------------------------------------------------------------------------- /predict/icons/target-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-11.png -------------------------------------------------------------------------------- /predict/icons/target-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-12.png -------------------------------------------------------------------------------- /predict/icons/target-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-13.png -------------------------------------------------------------------------------- /predict/icons/target-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-14.png -------------------------------------------------------------------------------- /predict/icons/target-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-15.png -------------------------------------------------------------------------------- /predict/icons/target-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-16.png -------------------------------------------------------------------------------- /predict/icons/target-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-17.png -------------------------------------------------------------------------------- /predict/icons/target-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-18.png -------------------------------------------------------------------------------- /predict/icons/target-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-red.png -------------------------------------------------------------------------------- /predict/images/target-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-1.png -------------------------------------------------------------------------------- /predict/images/target-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-10.png -------------------------------------------------------------------------------- /predict/images/target-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-11.png -------------------------------------------------------------------------------- /predict/images/target-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-12.png -------------------------------------------------------------------------------- /predict/images/target-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-13.png -------------------------------------------------------------------------------- /predict/images/target-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-14.png -------------------------------------------------------------------------------- /predict/images/target-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-15.png -------------------------------------------------------------------------------- /predict/images/target-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-16.png -------------------------------------------------------------------------------- /predict/images/target-17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-17.png -------------------------------------------------------------------------------- /predict/images/target-18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-18.png -------------------------------------------------------------------------------- /predict/images/target-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-2.png -------------------------------------------------------------------------------- /predict/images/target-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-3.png -------------------------------------------------------------------------------- /predict/images/target-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-4.png -------------------------------------------------------------------------------- /predict/images/target-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-5.png -------------------------------------------------------------------------------- /predict/images/target-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-6.png -------------------------------------------------------------------------------- /predict/images/target-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-7.png -------------------------------------------------------------------------------- /predict/images/target-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-8.png -------------------------------------------------------------------------------- /predict/images/target-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-9.png -------------------------------------------------------------------------------- /predict/icons/target-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-blue.png -------------------------------------------------------------------------------- /predict/icons/target-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/icons/target-yellow.png -------------------------------------------------------------------------------- /predict/images/balloon-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/balloon-sm.png -------------------------------------------------------------------------------- /predict/images/drag_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/drag_handle.png -------------------------------------------------------------------------------- /predict/images/pop-marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/pop-marker.png -------------------------------------------------------------------------------- /predict/images/target-1-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-1-sm.png -------------------------------------------------------------------------------- /predict/images/target-8-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-8-sm.png -------------------------------------------------------------------------------- /predict/images/target-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-blue.png -------------------------------------------------------------------------------- /predict/images/target-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-red.png -------------------------------------------------------------------------------- /predict/images/marker-sm-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/marker-sm-red.png -------------------------------------------------------------------------------- /predict/images/target-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/target-yellow.png -------------------------------------------------------------------------------- /predict/images/marker-sm-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/images/marker-sm-black.png -------------------------------------------------------------------------------- /cron/clear-pydap-cache-cronjob.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /tmp/pydap-cache 3 | ls /tmp/pydap-cache -1 | grep -v `date +"%Y%m%d"` | xargs rm -f 4 | 5 | -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-icons_2694e8_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-icons_2694e8_256x240.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-icons_3d80b3_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-icons_3d80b3_256x240.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-icons_72a7cf_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-icons_72a7cf_256x240.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-bg_flat_15_cd0a0a_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-bg_flat_15_cd0a0a_40x100.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-bg_glass_100_e4f1fb_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-bg_glass_100_e4f1fb_1x400.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-bg_glass_50_3baae3_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-bg_glass_50_3baae3_1x400.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-bg_glass_80_d7ebf9_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-bg_glass_80_d7ebf9_1x400.png -------------------------------------------------------------------------------- /pred_src/ini/README: -------------------------------------------------------------------------------- 1 | These files from from the iniparser project[1] and are released under the MIT 2 | license which may be found in the associated LICENSE file. 3 | 4 | [1] http://ndevilla.free.fr/iniparser/ 5 | -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-bg_highlight-hard_70_000000_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-bg_highlight-hard_70_000000_1x100.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-bg_highlight-soft_100_deedf7_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-bg_highlight-soft_100_deedf7_1x100.png -------------------------------------------------------------------------------- /predict/css/cupertino/images/ui-bg_highlight-soft_25_ffef8f_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonsowman/cusf-standalone-predictor/HEAD/predict/css/cupertino/images/ui-bg_highlight-soft_25_ffef8f_1x100.png -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | /* 2 | [ filename ] 3 | Copyright [ name ] 4 | 5 | This file is part of the Cambridge University Spaceflight landing 6 | prediction software for predicting the flight trajectories of 7 | latex radiosonde balloons. 8 | 9 | CU Spaceflight 2010 10 | http://www.cuspaceflight.co.uk 11 | */ 12 | 13 | -------------------------------------------------------------------------------- /pred_src/ini/README.orig: -------------------------------------------------------------------------------- 1 | 2 | Welcome to iniParser -- version 3.0b (beta) 3 | released 03 Jan 2008 4 | 5 | This modules offers parsing of ini files from the C level. 6 | See a complete documentation in HTML format, from this directory 7 | open the file html/index.html with any HTML-capable browser. 8 | 9 | Enjoy! 10 | 11 | N.Devillard 12 | Thu Jan 3 19:36:31 CET 2008 13 | -------------------------------------------------------------------------------- /cron/prune-predictions-cronjob.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PARAM="mtime" 4 | AGE="14" 5 | 6 | REPOROOT="/var/www/hab/predict/" 7 | DATADIR="predict/preds" 8 | 9 | echo `ls $REPOROOT$DATADIR/ -l | wc -l` "prediction scenarios found" 10 | echo `find $REPOROOT$DATADIR/* -maxdepth 0 -$PARAM +$AGE | wc -l` "of them had $PARAM of more than $AGE days" 11 | echo "Now deleting..." 12 | find $REPOROOT$DATADIR/* -maxdepth 0 -$PARAM +$AGE -exec rm -rf {} \; 13 | echo "Done deleting." 14 | echo `ls $REPOROOT$DATADIR/ -l | wc -l` "prediction scenarios remaining" 15 | -------------------------------------------------------------------------------- /predict/css/tipsy.css: -------------------------------------------------------------------------------- 1 | .tipsy { padding: 5px; font-size: 10px; opacity: 0.8; filter: alpha(opacity=80); background-repeat: no-repeat; background-image: url(../images/tipsy.gif); } 2 | .tipsy-inner { padding: 5px 8px 4px 8px; background-color: black; color: white; max-width: 200px; text-align: center; } 3 | .tipsy-inner { -moz-border-radius:3px; -webkit-border-radius:3px; } 4 | .tipsy-north { background-position: top center; } 5 | .tipsy-south { background-position: bottom center; } 6 | .tipsy-east { background-position: right center; } 7 | .tipsy-west { background-position: left center; } 8 | -------------------------------------------------------------------------------- /cron/crontab-example: -------------------------------------------------------------------------------- 1 | # /etc/crontab: system-wide crontab 2 | # Unlike any other crontab you don't have to run the `crontab' 3 | # command to install the new version when you edit this file 4 | # and files in /etc/cron.d. These files also have username fields, 5 | # that none of the other crontabs do. 6 | 7 | SHELL=/bin/sh 8 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 9 | 10 | # m h dom mon dow user command 11 | # SCRIPTS FOR CUSF-STANDALONE-PREDICTOR 12 | 0 1 * * * www-data /var/www/hab/predict/cron/clear-pydap-cache-cronjob.sh 13 | 10 1 * * * www-data /var/www/hab/predict/cron/prune-predictions-cronjob.sh 14 | 15 | -------------------------------------------------------------------------------- /predict/includes/config.inc.php: -------------------------------------------------------------------------------- 1 | 27 | -------------------------------------------------------------------------------- /pred_src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMake build file for the CUSF landing prediction software 2 | # 3 | # Original version: Rich Wareham 4 | 5 | # Use PkgConfig to find the glib libraries 6 | find_package(PkgConfig) 7 | 8 | pkg_check_modules(GLIB REQUIRED glib-2.0) 9 | 10 | include_directories(${GLIB_INCLUDE_DIRS}) 11 | link_directories(${GLIB_LIBRARY_DIRS}) 12 | 13 | add_executable(pred 14 | util/gopt.c 15 | util/getdelim.c 16 | util/random.h 17 | util/gopt.h 18 | util/getline.h 19 | util/getline.c 20 | util/getdelim.h 21 | util/random.c 22 | altitude.h 23 | wind/wind_file_cache.c 24 | wind/wind_file_cache.h 25 | wind/wind_file.c 26 | wind/wind_file.h 27 | altitude.c 28 | pred.c 29 | run_model.c 30 | pred.h 31 | run_model.h 32 | ini/iniparser.c 33 | ini/iniparser.h 34 | ini/dictionary.h 35 | ini/dictionary.c 36 | ) 37 | 38 | target_link_libraries(pred ${GLIB_LIBRARIES} -lm) 39 | -------------------------------------------------------------------------------- /predict/js/pred/pred-config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * CUSF Landing Prediction Version 2 3 | * http://www.cuspaceflight.co.uk 4 | * 5 | * Jon Sowman 2010 6 | * jon@hexoc.com 7 | * http://www.hexoc.com 8 | * 9 | * http://github.com/jonsowman/cusf-standalone-predictor 10 | * 11 | */ 12 | 13 | // JavaScript configuration options in this file 14 | // To keep the index page JS-free 15 | 16 | var ajaxEventHandle; 17 | var current_uuid = '0'; 18 | 19 | var map; 20 | var kmlLayer = null; 21 | var map_items = []; 22 | var launch_img = "images/target-1-sm.png"; 23 | var land_img = "images/target-8-sm.png"; 24 | var burst_img = "images/pop-marker.png"; 25 | var clickListener; 26 | var clickMarker; 27 | 28 | // Polling progress parameters 29 | var ajaxTimeout = 500; 30 | var maxAjaxTimeout = 2000; 31 | var deltaAjaxTimeout = 500; 32 | var stdPeriod = 2000; // standard 33 | var hlPeriod = 10000; // high latency 34 | var hlTimeout = 5000; // high latency 35 | -------------------------------------------------------------------------------- /pred_src/pred.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rob Anderson 6 | // Modified by Fergus Noble 7 | // 8 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 9 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 10 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 11 | // PARTICULAR PURPOSE. 12 | // -------------------------------------------------------------- 13 | 14 | #ifndef __PRED_H__ 15 | #define __PRED_H__ 16 | 17 | #define VERSION "0.0.1" 18 | 19 | // write a position entry into the output files 20 | void write_position(float lat, float lng, float alt, int timestamp); 21 | 22 | // start and finish KML files, basically just write header and footer in 23 | void start_kml(); 24 | void finish_kml(); 25 | 26 | #endif // __PRED_H__ 27 | 28 | 29 | -------------------------------------------------------------------------------- /pred_src/util/random.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rich Wareham 6 | // 7 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 8 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 9 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 10 | // PARTICULAR PURPOSE. 11 | // -------------------------------------------------------------- 12 | 13 | #ifndef __RANDOM_H__ 14 | #define __RANDOM_H__ 15 | 16 | // Return a random sample drawn from the normal distribution with mean mu and 17 | // variance sigma2. If loglik is non-NULL, *loglik is set to the log-likelihood 18 | // of drawing that sample. 19 | float random_sample_normal(float mu, float sigma2, float *loglik); 20 | 21 | #endif /* __RANDOM_H__ */ 22 | 23 | // vim:sw=4:ts=4:et:cindent 24 | -------------------------------------------------------------------------------- /predict/css/calc.css: -------------------------------------------------------------------------------- 1 | #mb { 2 | width: 170px; 3 | text-align: center; 4 | } 5 | 6 | #ui { 7 | float: left; 8 | } 9 | 10 | #input_table { 11 | border-spacing: 0; 12 | } 13 | 14 | #input_table td { 15 | padding-top: 3px; 16 | padding-bottom: 3px; 17 | padding-left: 3px; 18 | padding-right: 3px; 19 | } 20 | 21 | .constants_header { 22 | font-weight: bold; 23 | } 24 | 25 | .input_label { 26 | text-align: center; 27 | } 28 | 29 | .input_field { 30 | text-align: center; 31 | } 32 | 33 | .input_instruction { 34 | font-weight: bold; 35 | text-align: center; 36 | } 37 | 38 | .input_row { 39 | } 40 | 41 | .input_row td { 42 | text-align: center; 43 | } 44 | 45 | .warning_row { 46 | color: #f55; 47 | font-size: 0.8em; 48 | font-weight: bold; 49 | padding-bottom: 5px; 50 | text-align: center; 51 | } 52 | 53 | .input_warning { 54 | color: red; 55 | } 56 | 57 | .output_row { 58 | } 59 | 60 | .output_label { 61 | text-align: right; 62 | } 63 | 64 | .output_data { 65 | text-align: left; 66 | } 67 | 68 | -------------------------------------------------------------------------------- /predict/sites.json: -------------------------------------------------------------------------------- 1 | { 2 | "Churchill": { 3 | "latitude" : 52.2135, 4 | "longitude" : 0.0964, 5 | "altitude" : 0 6 | }, 7 | "Elsworth": { 8 | "latitude" : 52.2511, 9 | "longitude" : -0.0927, 10 | "altitude" : 0 11 | }, 12 | "Adelaide": { 13 | "altitude" : 0, 14 | "latitude" : -34.9499, 15 | "longitude" : 138.5194 16 | }, 17 | "Brightwalton": { 18 | "altitude" : 0, 19 | "latitude" : 51.51143, 20 | "longitude" : -1.38870 21 | }, 22 | "Boston Spa": { 23 | "latitude" : 53.8997, 24 | "longitude" : -1.3629, 25 | "altitude" : 0 26 | }, 27 | "Wistow": { 28 | "altitude" : 0, 29 | "latitude" : -35.1217, 30 | "longitude" : 138.8503 31 | }, 32 | "Cookstown": { 33 | "altitude" : 60, 34 | "latitude" : 54.632162, 35 | "longitude" : -6.757982 36 | }, 37 | "Preston St Mary": { 38 | "latitude" : 52.1215, 39 | "longitude" : 0.8078, 40 | "altitude" : 70 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /pred_src/ini/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2000-2007 by Nicolas Devillard. 2 | MIT License 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a 5 | copy of this software and associated documentation files (the "Software"), 6 | to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /pred_src/util/getline.c: -------------------------------------------------------------------------------- 1 | /* getline.c --- Implementation of replacement getline function. 2 | Copyright (C) 2005 Free Software Foundation, Inc. 3 | 4 | This program is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU General Public License as 6 | published by the Free Software Foundation; either version 2, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | 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; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 17 | 02110-1301, USA. */ 18 | 19 | /* Written by Simon Josefsson. */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | # include 23 | #endif 24 | 25 | #include "getdelim.h" 26 | #include "getline.h" 27 | 28 | ssize_t 29 | getline (char **lineptr, size_t *n, FILE *stream) 30 | { 31 | return getdelim (lineptr, n, '\n', stream); 32 | } 33 | -------------------------------------------------------------------------------- /pred_src/util/getline.h: -------------------------------------------------------------------------------- 1 | /* getline.h --- Prototype for replacement getline function. 2 | Copyright (C) 2005 Free Software Foundation, Inc. 3 | 4 | This program is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU General Public License as 6 | published by the Free Software Foundation; either version 2, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | 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; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 17 | 02110-1301, USA. */ 18 | 19 | /* Written by Simon Josefsson. */ 20 | 21 | /* Get size_t, FILE, ssize_t. And getline, if available. */ 22 | # include 23 | # include 24 | # include 25 | 26 | #if !HAVE_DECL_GETLINE 27 | ssize_t getline (char **lineptr, size_t *n, FILE *stream); 28 | #endif /* !HAVE_GETLINE */ 29 | -------------------------------------------------------------------------------- /pred_src/util/getdelim.h: -------------------------------------------------------------------------------- 1 | /* getdelim.h --- Prototype for replacement getdelim function. 2 | Copyright (C) 2005 Free Software Foundation, Inc. 3 | 4 | This program is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU General Public License as 6 | published by the Free Software Foundation; either version 2, or (at 7 | your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but 10 | WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 | 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; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 17 | 02110-1301, USA. */ 18 | 19 | /* Written by Simon Josefsson. */ 20 | 21 | /* Get size_t, FILE, ssize_t. And getdelim, if available. */ 22 | # include 23 | # include 24 | # include 25 | 26 | #if !HAVE_DECL_GETDELIM 27 | ssize_t getdelim (char **lineptr, size_t *n, int delimiter, FILE *stream); 28 | #endif /* !HAVE_GETDELIM */ 29 | -------------------------------------------------------------------------------- /predict/includes/statsd.php: -------------------------------------------------------------------------------- 1 | 6 | * https://raw.github.com/seejohnrun/php-statsd/master/libraries/statsd.php 7 | */ 8 | class StatsD { 9 | 10 | private $host, $port; 11 | 12 | // Instantiate a new client 13 | public function __construct($host = 'localhost', $port = 8125) { 14 | $this->host = $host; 15 | $this->port = $port; 16 | } 17 | 18 | // Record timing 19 | public function timing($key, $time, $rate = 1) { 20 | $this->send("$key:$time|ms", $rate); 21 | } 22 | 23 | // Time something 24 | public function time_this($key, $callback, $rate = 1) { 25 | $begin = microtime(true); 26 | $callback(); 27 | $time = floor((microtime(true) - $begin) * 1000); 28 | // And record 29 | $this->timing($key, $time, $rate); 30 | } 31 | 32 | // Record counting 33 | public function counting($key, $amount = 1, $rate = 1) { 34 | $this->send("$key:$amount|c", $rate); 35 | } 36 | 37 | // Send 38 | private function send($value, $rate) { 39 | $fp = fsockopen('udp://' . $this->host, $this->port, $errno, $errstr); 40 | // Will show warning if not opened, and return false 41 | if ($fp) { 42 | fwrite($fp, "$value|@$rate"); 43 | fclose($fp); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /predict/admin.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | CUSF Landing Predictor V2.0 Admin 4 | 5 | 8 | 9 |

Recent Predictions

10 | 14 ) { 16 | $limit = 1; 17 | } else { 18 | $limit = $_GET['limit']; 19 | } 20 | $threshold = time() - $limit*60*60*24; 21 | 22 | $dirs = scandir(PREDS_PATH); 23 | foreach( $dirs as $dir ) { 24 | if ( is_dir(PREDS_PATH . $dir) && $dir != '.' && $dir != '..' && filemtime(PREDS_PATH.$dir) > $threshold ) 25 | $uuid_list[] = $dir; 26 | } 27 | 28 | echo '

' . $limit . ' days old or newer

'; 29 | 30 | $i=1; 31 | echo ''; 32 | echo '' 33 | . ''; 34 | foreach( $uuid_list as $uuid ) { 35 | $scenario = parse_ini_file( PREDS_PATH . $uuid . "/" . SCENARIO_FILE ); 36 | echo ''; 37 | echo ''; 38 | echo ''; 39 | echo ''; 40 | echo ''; 41 | echo ''; 42 | echo ''; 44 | $i++; 45 | } 46 | echo '
IndexUUIDLatLonAscDescBurstView
' . $i . '' . $uuid . '' . $scenario['latitude'] . '' . $scenario['longitude'] . '' . $scenario['ascent-rate'] . '' . $scenario['descent-rate'] . '' . $scenario['burst-altitude'] . 'View'; 43 | echo '
'; 47 | 48 | ?> 49 | 50 | -------------------------------------------------------------------------------- /pred_src/util/random.c: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rich Wareham 6 | // 7 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 8 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 9 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 10 | // PARTICULAR PURPOSE. 11 | // -------------------------------------------------------------- 12 | 13 | #include "random.h" 14 | 15 | #include 16 | #include 17 | 18 | // Sample from a normal distribution with zero mean and unit variance. 19 | // See http://en.wikipedia.org/wiki/Normal_distribution 20 | // #Generating_values_for_normal_random_variables 21 | static float _random_sample_normal_intl(float* loglik) 22 | { 23 | double u, v = 0.0; 24 | static const double k = 0.918938533204673; // = 0.5 * (log(2) + log(pi)), see below. 25 | 26 | u = g_random_double(); 27 | v = g_random_double(); 28 | v = sqrt(-2.0 * log(u)) * cos(2.0 * G_PI * v); 29 | 30 | // actual likelihood is 1/sqrt(2*pi) exp(-(x^2)) since mu = 0 and sigma^2 = 1. 31 | // log-likelihood is therefore: 32 | // \ell(x) = - (x^2) - 0.5 * (log(2) + log(pi)) = - (x^2) - k 33 | 34 | if(loglik) 35 | *loglik = (float) ( - (v*v) - k ); 36 | 37 | return v; 38 | } 39 | 40 | float random_sample_normal(float mu, float sigma2, float *loglik) 41 | { 42 | // Sample from our base case. 43 | float v = _random_sample_normal_intl(loglik); 44 | 45 | // Transform into appropriate range. 46 | v *= sqrt(sigma2); 47 | v += mu; 48 | 49 | return v; 50 | } 51 | 52 | // vim:sw=4:ts=4:et:cindent 53 | -------------------------------------------------------------------------------- /pred_src/cmake_install.cmake: -------------------------------------------------------------------------------- 1 | # Install script for directory: /var/www/html/habhub/cusf-standalone-predictor/pred_src 2 | 3 | # Set the install prefix 4 | IF(NOT DEFINED CMAKE_INSTALL_PREFIX) 5 | SET(CMAKE_INSTALL_PREFIX "/usr/local") 6 | ENDIF(NOT DEFINED CMAKE_INSTALL_PREFIX) 7 | STRING(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") 8 | 9 | # Set the install configuration name. 10 | IF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) 11 | IF(BUILD_TYPE) 12 | STRING(REGEX REPLACE "^[^A-Za-z0-9_]+" "" 13 | CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") 14 | ELSE(BUILD_TYPE) 15 | SET(CMAKE_INSTALL_CONFIG_NAME "") 16 | ENDIF(BUILD_TYPE) 17 | MESSAGE(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") 18 | ENDIF(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) 19 | 20 | # Set the component getting installed. 21 | IF(NOT CMAKE_INSTALL_COMPONENT) 22 | IF(COMPONENT) 23 | MESSAGE(STATUS "Install component: \"${COMPONENT}\"") 24 | SET(CMAKE_INSTALL_COMPONENT "${COMPONENT}") 25 | ELSE(COMPONENT) 26 | SET(CMAKE_INSTALL_COMPONENT) 27 | ENDIF(COMPONENT) 28 | ENDIF(NOT CMAKE_INSTALL_COMPONENT) 29 | 30 | # Install shared libraries without execute permission? 31 | IF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) 32 | SET(CMAKE_INSTALL_SO_NO_EXE "0") 33 | ENDIF(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) 34 | 35 | IF(CMAKE_INSTALL_COMPONENT) 36 | SET(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") 37 | ELSE(CMAKE_INSTALL_COMPONENT) 38 | SET(CMAKE_INSTALL_MANIFEST "install_manifest.txt") 39 | ENDIF(CMAKE_INSTALL_COMPONENT) 40 | 41 | FILE(WRITE "/var/www/html/habhub/cusf-standalone-predictor/pred_src/${CMAKE_INSTALL_MANIFEST}" "") 42 | FOREACH(file ${CMAKE_INSTALL_MANIFEST_FILES}) 43 | FILE(APPEND "/var/www/html/habhub/cusf-standalone-predictor/pred_src/${CMAKE_INSTALL_MANIFEST}" "${file}\n") 44 | ENDFOREACH(file) 45 | -------------------------------------------------------------------------------- /pred_src/wind/wind_file.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rich Wareham 6 | // 7 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 8 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 9 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 10 | // PARTICULAR PURPOSE. 11 | // -------------------------------------------------------------- 12 | 13 | #ifndef __WIND_FILE_H__ 14 | #define __WIND_FILE_H__ 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif // __cplusplus 19 | 20 | // An opaque type representing the cache itself. 21 | typedef struct wind_file_s wind_file_t; 22 | 23 | // An opaque type representing a cache entry. 24 | typedef struct wind_file_entry_s wind_file_entry_t; 25 | 26 | // Open 'file' and parse contents. Return NULL on failure. 27 | wind_file_t *wind_file_new (const char *file); 28 | 29 | // Free resources associated with 'file'. 30 | void wind_file_free (wind_file_t *file); 31 | 32 | int wind_file_get_wind (wind_file_t *file, 33 | float lat, 34 | float lon, 35 | float height, 36 | float *windu, 37 | float *windv, 38 | float *windusq, 39 | float *windvsq); 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif // __cplusplus 44 | 45 | #endif // __WIND_FILE_H__ 46 | 47 | // Data for God's own editor. 48 | // vim:sw=8:ts=8:et:cindent 49 | -------------------------------------------------------------------------------- /pred_src/run_model.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rob Anderson 6 | // Modified by Fergus Noble 7 | // 8 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 9 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 10 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 11 | // PARTICULAR PURPOSE. 12 | // -------------------------------------------------------------- 13 | 14 | #ifndef __RUN_MODEL_H__ 15 | #define __RUN_MODEL_H__ 16 | 17 | #include "wind/wind_file_cache.h" 18 | #include "altitude.h" 19 | 20 | // run the model 21 | int run_model(wind_file_cache_t* cache, altitude_model_t* alt_model, 22 | float initial_lat, float initial_lng, float initial_alt, 23 | long int initial_timestamp, float rmswinderror); 24 | 25 | #define TIMESTEP 1 // in seconds 26 | #define LOG_DECIMATE 50 // write entry to output files every x timesteps 27 | 28 | #define METRES_TO_DEGREES 0.00000899289281755 // one metre corresponds to this many degrees latitude 29 | #define DEGREES_TO_METRES 111198.92345 // one degree latitude corresponds to this many metres 30 | #define DEGREES_TO_RADIANS 0.0174532925 // 1 degree is this many radians 31 | 32 | // get the wind values in the u and v directions at a point in space and time from the dataset data 33 | // we interpolate lat, lng, alt and time. The GRIB data only contains pressure levels so we first 34 | // determine which pressure levels straddle to our desired altitude and then interpolate between them 35 | int get_wind(wind_file_cache_t* cache, float lat, float lng, float alt, long int timestamp, float* wind_v, float* wind_u, float *wind_var); 36 | // note: get_wind will likely call load_data and load a different tile into data, so just be careful that data could be pointing 37 | // somewhere else after running get_wind 38 | 39 | #endif // __RUN_MODEL_H__ 40 | 41 | -------------------------------------------------------------------------------- /pred_src/altitude.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rob Anderson 6 | // Modified by Fergus Noble 7 | // 8 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 9 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 10 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 11 | // PARTICULAR PURPOSE. 12 | // -------------------------------------------------------------- 13 | 14 | #ifndef __ALTITUDE_H__ 15 | #define __ALTITUDE_H__ 16 | 17 | typedef struct altitude_model_s altitude_model_t; 18 | 19 | // create an altitude/time model with given parameters, must be called before 20 | // calling run_model if descent_mode is DESCENT_MODE_DESCENDING then we start 21 | // off with the balloon descending i.e. after burst 22 | altitude_model_t *altitude_model_new (int descent_mode, 23 | float burst_alt, 24 | float ascent_rate, 25 | float drag_coeff); 26 | 27 | // free resources associated with the specified altitude model. 28 | void altitude_model_free (altitude_model_t *model); 29 | 30 | // returns the altitude corresponding to a certain time into the flight (in seconds) 31 | // the result it stored in the alt variable. 32 | // the contents of alt when the function is called with time_into_flight = 0 33 | // will be taken as the starting altitude returns 1 normally and 0 when the 34 | // flight has terminated 35 | int altitude_model_get_altitude 36 | (altitude_model_t *model, 37 | int time_into_flight, 38 | float *alt); 39 | 40 | 41 | 42 | // it seems like overkill to do it this way but it is in preparation for being able to load in 43 | // arbitrary altitude/time profiles from a file 44 | 45 | #define DESCENT_MODE_DESCENDING 1 46 | #define DESCENT_MODE_NORMAL 0 47 | 48 | #endif // __ALTITUDE_H__ 49 | 50 | -------------------------------------------------------------------------------- /pred_src/util/gopt.h: -------------------------------------------------------------------------------- 1 | /* gopt.h version 8.1: tom.viza@gmail.com PUBLIC DOMAIN 2003-8 */ 2 | /* 3 | I, Tom Vajzovic, am the author of this software and its documentation and 4 | permanently abandon all copyright and other intellectual property rights in 5 | them, including the right to be identified as the author. 6 | 7 | I am fairly certain that this software does what the documentation says it 8 | does, but I cannot guarantee that it does, or that it does what you think it 9 | should, and I cannot guarantee that it will not have undesirable side effects. 10 | 11 | You are free to use, modify and distribute this software as you please, but 12 | you do so at your own risk. If you remove or hide this warning then you are 13 | responsible for any problems encountered by people that you make the software 14 | available to. 15 | 16 | Before modifying or distributing this software I ask that you would please 17 | read http://www.purposeful.co.uk/tfl/ 18 | */ 19 | 20 | #ifndef GOPT_H_INCLUDED 21 | #define GOPT_H_INCLUDED 22 | 23 | #define GOPT_ONCE 0 24 | #define GOPT_REPEAT 1 25 | #define GOPT_NOARG 0 26 | #define GOPT_ARG 2 27 | 28 | typedef struct { int k; int f; const char *s; const char*const*l; } kfsl; 29 | #define gopt_start(...) (const void*)( const kfsl[]){ __VA_ARGS__, {0}} 30 | #define gopt_option(k,f,s,l) { k, f, s, l } 31 | #define gopt_shorts( ... ) (const char*)(const char[]){ __VA_ARGS__, 0 } 32 | #define gopt_longs( ... ) (const char**)(const char*[]){ __VA_ARGS__, NULL } 33 | 34 | 35 | void *gopt_sort( int *argc, const char **argv, const void *opt_specs ); 36 | /* returns a pointer for use in the following calls 37 | * prints to stderr and call exit() on error 38 | */ 39 | size_t gopt( const void *opts, int key ); 40 | /* returns the number of times the option was specified 41 | * which will be 0 or 1 unless GOPT_REPEAT was used 42 | */ 43 | size_t gopt_arg( const void *opts, int key, const char **arg ); 44 | /* returns the number of times the option was specified 45 | * writes a pointer to the option argument from the first (or only) occurance to *arg 46 | */ 47 | const char *gopt_arg_i( const void *opts, int key, size_t i ); 48 | /* returns a pointer to the ith (starting at zero) occurance 49 | * of the option, or NULL if it was not specified that many times 50 | */ 51 | size_t gopt_args( const void *opts, int key, const char **args, size_t args_len ); 52 | /* returns the number of times the option was specified 53 | * writes pointers to the option arguments in the order of occurance to args[]. 54 | * writes at most args_len pointers 55 | * if the return value is less than args_len, also writes a null pointer 56 | */ 57 | void gopt_free( void *opts ); 58 | /* releases memory allocated in the corresponding call to gopt_sort() 59 | * opts can no longer be used 60 | */ 61 | #endif /* GOPT_H_INCLUDED */ 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CUSF Standalone Predictor - Version 2 2 | 3 | Cambridge University Spaceflight landing predictor - a web-based tool for predicting the flight path and landing location of latex meteorological sounding balloons. 4 | 5 | ## Install 6 | 7 | The source for the predictor itself is in `pred_src/` and instructions for building it can be found there. 8 | 9 | The following items need to be executable (`chmod +x ./predict.py`) by the user under which the predictor runs: 10 | 11 | * `predict.py` 12 | * `pred_src/pred` (once compiled) 13 | * `cron/clear-pydap-cache-cronjob.sh` 14 | * `cron/purge-predictions-cronjob.sh` 15 | 16 | The `predict/preds/` and `gfs/` directories need to have rwx access by the PHP interpreter and the `predict.py` python script. You will need to install the python dependencies listed in requirements.txt. In the case of PyDAP, the exact version is important; the easiest way is: 17 | 18 | $ pip install -r requirements.txt 19 | 20 | Other than that, just clone this repo to a non web-accessible folder and create symlinks to the `predict/` directory in the repo. 21 | 22 | There are useful configuration options in `predict/includes/config.inc.php`. 23 | 24 | ## Information 25 | 26 | The two shell scripts in the `cron/` directory should both be run daily. `clear-pydap-cache-cronjob.sh` clears the cache used by pydap so that old data does not build up. `purge-predictions-cronjob.sh` deletes scenarios and predictions not accessed or modified within the last 7 days. Re-running a prediction for a scenario will therefore reset its time to live to 7 more days. 27 | 28 | The directory names are UUIDs comprised of an SHA1 hash of the launch parameters, and re-running predictions will overwrite data in the existing directory, rather than create a new one. 29 | 30 | We use GFS data provided by the NOAA, accessed via NDAP and their [NOMADS](http://nomads.ncep.noaa.gov) distribution system. The [1.0x1.0 degree data](http://nomads.ncep.noaa.gov/txt_descriptions/GFS_high_resolution_doc.shtml) (26 vertical pressure levels) is used for standard predictions, and the [0.5x0.5 degree data](http://nomads.ncep.noaa.gov/txt_descriptions/GFS_half_degree_doc.shtml) (47 vertical pressure levels) is used for the high definition (HD) predictions. 31 | 32 | ## License 33 | 34 | This work 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 2 of the License, or any later version. This work 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. 35 | 36 | ## Credits & Acknowledgments 37 | 38 | Credit as detailed in individual files, but notably: 39 | 40 | * Rich Wareham - The new predictor and the hourly predictor system 41 | * Fergus Noble, Ed Moore and many others 42 | 43 | Adam Greig - [http://www.randomskk.net](http://www.randomskk.net) - [random@randomskk.net](mailto:random@randomskk.net) 44 | Jon Sowman - [http://www.hexoc.com](http://www.hexoc.com) - [jon@hexoc.com](mailto:jon@hexoc.com) 45 | 46 | Copyright Cambridge University Spaceflight 2009-2011 - All Rights Reserved 47 | -------------------------------------------------------------------------------- /pred_src/util/getdelim.c: -------------------------------------------------------------------------------- 1 | /* getdelim.c --- Implementation of replacement getdelim function. 2 | Copyright (C) 1994, 1996, 1997, 1998, 2001, 2003, 2005 Free 3 | Software Foundation, Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License as 7 | published by the Free Software Foundation; either version 2, or (at 8 | your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, but 11 | WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18 | 02110-1301, USA. */ 19 | 20 | /* Ported from glibc by Simon Josefsson. */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | # include 24 | #endif 25 | 26 | #include "getdelim.h" 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #ifndef SIZE_MAX 33 | # define SIZE_MAX ((size_t) -1) 34 | #endif 35 | #ifndef SSIZE_MAX 36 | # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) 37 | #endif 38 | #if !HAVE_FLOCKFILE 39 | # undef flockfile 40 | # define flockfile(x) ((void) 0) 41 | #endif 42 | #if !HAVE_FUNLOCKFILE 43 | # undef funlockfile 44 | # define funlockfile(x) ((void) 0) 45 | #endif 46 | 47 | /* Read up to (and including) a DELIMITER from FP into *LINEPTR (and 48 | NUL-terminate it). *LINEPTR is a pointer returned from malloc (or 49 | NULL), pointing to *N characters of space. It is realloc'ed as 50 | necessary. Returns the number of characters read (not including 51 | the null terminator), or -1 on error or EOF. */ 52 | 53 | ssize_t 54 | getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp) 55 | { 56 | ssize_t result; 57 | size_t cur_len = 0; 58 | 59 | if (lineptr == NULL || n == NULL || fp == NULL) 60 | { 61 | errno = EINVAL; 62 | return -1; 63 | } 64 | 65 | flockfile (fp); 66 | 67 | if (*lineptr == NULL || *n == 0) 68 | { 69 | *n = 120; 70 | *lineptr = (char *) malloc (*n); 71 | if (*lineptr == NULL) 72 | { 73 | result = -1; 74 | goto unlock_return; 75 | } 76 | } 77 | 78 | for (;;) 79 | { 80 | int i; 81 | 82 | i = getc (fp); 83 | if (i == EOF) 84 | { 85 | result = -1; 86 | break; 87 | } 88 | 89 | /* Make enough space for len+1 (for final NUL) bytes. */ 90 | if (cur_len + 1 >= *n) 91 | { 92 | size_t needed_max = 93 | SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX; 94 | size_t needed = 2 * *n + 1; /* Be generous. */ 95 | char *new_lineptr; 96 | 97 | if (needed_max < needed) 98 | needed = needed_max; 99 | if (cur_len + 1 >= needed) 100 | { 101 | result = -1; 102 | goto unlock_return; 103 | } 104 | 105 | new_lineptr = (char *) realloc (*lineptr, needed); 106 | if (new_lineptr == NULL) 107 | { 108 | result = -1; 109 | goto unlock_return; 110 | } 111 | 112 | *lineptr = new_lineptr; 113 | *n = needed; 114 | } 115 | 116 | (*lineptr)[cur_len] = i; 117 | cur_len++; 118 | 119 | if (i == delimiter) 120 | break; 121 | } 122 | (*lineptr)[cur_len] = '\0'; 123 | result = cur_len ? cur_len : result; 124 | 125 | unlock_return: 126 | funlockfile (fp); 127 | return result; 128 | } 129 | -------------------------------------------------------------------------------- /pred_src/wind/wind_file_cache.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rich Wareham 6 | // 7 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 8 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 9 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 10 | // PARTICULAR PURPOSE. 11 | // -------------------------------------------------------------- 12 | 13 | #ifndef __WIND_FILES_H__ 14 | #define __WIND_FILES_H__ 15 | 16 | #include "wind_file.h" 17 | 18 | // A cache which scans the wind data directory for data files, tries to read 19 | // the header and parse out their timestamp and window information. It then 20 | // allows one to query for files closest in time and space for a specified 21 | // latitude/longitude/time. 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif // __cplusplus 26 | 27 | // An opaque type representing the cache itself. 28 | typedef struct wind_file_cache_s wind_file_cache_t; 29 | 30 | // An opaque type representing a cache entry. 31 | typedef struct wind_file_cache_entry_s wind_file_cache_entry_t; 32 | 33 | // Scan 'directory' for wind files. Return a new cache. 34 | wind_file_cache_t *wind_file_cache_new (const char *directory); 35 | 36 | // Free resources associated with 'cache'. 37 | void wind_file_cache_free (wind_file_cache_t *cache); 38 | 39 | // Search for a cache entry closest to the specified lat, lon and time. 40 | // *earlier and *later are set to the nearest cache entries which are 41 | // (respectively) earlier and later. 42 | void wind_file_cache_find_entry 43 | (wind_file_cache_t *cache, 44 | float lat, 45 | float lon, 46 | unsigned long timestamp, 47 | wind_file_cache_entry_t **earlier, 48 | wind_file_cache_entry_t **later); 49 | 50 | // Return non-zero if the cache entry specifies contains the latitude 51 | // and longitude. 52 | int wind_file_cache_entry_contains_point 53 | (wind_file_cache_entry_t *entry, 54 | float lat, 55 | float lon); 56 | 57 | // Return a string which gives the full path to the wind file 58 | // corresponding to 'entry'. This should not be freed since it 59 | // is owned by the cache itself. 60 | const char* wind_file_cache_entry_file_path 61 | (wind_file_cache_entry_t *entry); 62 | 63 | // Return the timestamp of the specified cache entry. 64 | unsigned int wind_file_cache_entry_timestamp 65 | (wind_file_cache_entry_t *entry); 66 | 67 | // Return the file for of the specified cache entry loading it if 68 | // necessary. 69 | wind_file_t* wind_file_cache_entry_file 70 | (wind_file_cache_entry_t *entry); 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif // __cplusplus 75 | 76 | #endif // __WIND_FILES_H__ 77 | 78 | // Data for God's own editor. 79 | // vim:sw=8:ts=8:et:cindent 80 | -------------------------------------------------------------------------------- /predict/js/pred/pred-ui.js: -------------------------------------------------------------------------------- 1 | /* 2 | * CUSF Landing Prediction Version 2 3 | * Jon Sowman 2010 4 | * jon@hexoc.com 5 | * http://www.hexoc.com 6 | * 7 | * http://github.com/jonsowman/cusf-standalone-predictor 8 | * 9 | * This file contains javascript functions related to the handling 10 | * of the user interface for the predictor. 11 | * 12 | */ 13 | 14 | // Initialise the UI - this must be called on document ready 15 | function initUI() { 16 | // Make UI elements such as windows draggable 17 | $("#input_form").draggable({containment: '#map_canvas', handle: 18 | 'img.handle', snap: '#map_canvas'}); 19 | $("#scenario_info").draggable({containment: '#map_canvas', handle: 20 | 'img.handle', snap: '#map_canvas'}); 21 | $("#location_save").draggable({containment: '#map_canvas', handle: 22 | 'img.handle', snap: '#map_canvas'}); 23 | $("#location_save_local").draggable({containment: '#map_canvas', handle: 24 | 'img.handle', snap: '#map_canvas'}); 25 | $("#burst-calc-wrapper").draggable({containment: '#map_canvas', handle: 26 | 'img.handle', snap: '#map_canvas'}); 27 | 28 | // Activate buttons to jqueryui styling 29 | $("#run_pred_btn").button(); 30 | $("#req_sub_btn").button(); 31 | $("#burst-calc-use").button(); 32 | $("#burst-calc-close").button(); 33 | $("#burst-calc-advanced-show").button(); 34 | $("#burst-calc-advanced-hide").button(); 35 | } 36 | 37 | // Throw an error window containing and a 'close' link 38 | function throwError(data) { 39 | $("#error_message").html(data); 40 | $("#error_window").fadeIn(); 41 | } 42 | 43 | // Reset the GUI to a onLoad state ready for a new prediction to be shown 44 | function resetGUI() { 45 | $("#status_message").fadeOut(500); 46 | $("#error_window").fadeOut(500); 47 | // now clear the status window 48 | $("#prediction_status").html(""); 49 | $("#prediction_progress").progressbar("options", "value", 0); 50 | $("#prediction_percent").html(""); 51 | $("#cursor_pred").hide(); 52 | // bring the input form back up 53 | toggleWindow("input_form", null, null, null, "show"); 54 | toggleWindow("scenario_info", null, null, null, "show"); 55 | // un-fade the map canvas 56 | $("#map_canvas").fadeTo(1500, 1); 57 | } 58 | 59 | // Append a line to the debug window and scroll the window to the bottom 60 | // Optional boolean second argument will clear the debug window if TRUE 61 | function appendDebug(appendage, clear) { 62 | if ( clear == null ){ 63 | var curr = $("#debuginfo").html(); 64 | curr += "
" + appendage; 65 | $("#debuginfo").html(curr); 66 | } else { 67 | $("#debuginfo").html(""); 68 | } 69 | // keep the debug window scrolled to bottom 70 | scrollToBottom("scenario_template"); 71 | } 72 | 73 | // A function to scroll a scrollable
all the way to the bottom 74 | function scrollToBottom(div_id) { 75 | $("#"+div_id).animate({scrollTop: $("#"+div_id)[0].scrollHeight}); 76 | } 77 | 78 | // Show or hide GUI windows, can either "toggle", or force hide/show 79 | // Takes the window name, the linker ID, the event handlers for 80 | // 'onhide' and 'onshow', and a boolean 'force' parameter 81 | function toggleWindow(window_name, linker, onhide, onshow, force) { 82 | if ( force == null ) { 83 | if( $("#"+window_name).css('display') != "none" ){ 84 | $("#"+window_name+"").hide("slide", { direction: "down" }, 500); 85 | $("#"+linker).html(onhide); 86 | } else { 87 | $("#"+window_name).show("slide", { direction: "down" }, 500); 88 | $("#"+linker).html(onshow); 89 | } 90 | } else if ( force == "hide" ) { 91 | if( $("#"+window_name).css('display') != "none" ){ 92 | $("#"+window_name+"").hide("slide", { direction: "down" }, 500); 93 | $("#"+linker).html(onhide); 94 | } 95 | } else if ( force == "show") { 96 | if( $("#"+window_name).css('display') == "none" ){ 97 | $("#"+window_name).show("slide", { direction: "down" }, 500); 98 | $("#"+linker).html(onshow); 99 | } 100 | } else { 101 | appendDebug("toggleWindow force parameter unrecognised"); 102 | } 103 | } 104 | 105 | // Set the selected item to "Other" in the launch locations selector 106 | function SetSiteOther() { 107 | $("#site").val("Other"); 108 | } 109 | 110 | -------------------------------------------------------------------------------- /pred_src/altitude.c: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rob Anderson 6 | // Modified by Fergus Noble 7 | // 8 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 9 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 10 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 11 | // PARTICULAR PURPOSE. 12 | // -------------------------------------------------------------- 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "pred.h" 19 | #include "altitude.h" 20 | #include "run_model.h" 21 | 22 | // get density of atmosphere at a given altitude 23 | // uses NASA model from http://www.grc.nasa.gov/WWW/K-12/airplane/atmosmet.html 24 | // units of degrees celcius, metres, KPa and Kg/metre cubed 25 | static float get_density(float altitude); 26 | 27 | #define G 9.8 28 | 29 | struct altitude_model_s 30 | { 31 | float burst_altitude; 32 | float ascent_rate; 33 | float drag_coeff; 34 | int descent_mode; 35 | 36 | float initial_alt; 37 | int burst_time; 38 | }; 39 | 40 | altitude_model_t* 41 | altitude_model_new(int dec_mode, float burst_alt, float asc_rate, float drag_co) 42 | { 43 | altitude_model_t* self = (altitude_model_t*)malloc(sizeof(altitude_model_t)); 44 | 45 | // this function doesn't do anything much at the moment 46 | // but will prove useful in the future 47 | 48 | self->burst_altitude = burst_alt; 49 | self->ascent_rate = asc_rate; 50 | self->drag_coeff = drag_co; 51 | self->descent_mode = dec_mode; 52 | 53 | return self; 54 | } 55 | 56 | void 57 | altitude_model_free(altitude_model_t* self) 58 | { 59 | if(!self) 60 | return; 61 | 62 | free(self); 63 | } 64 | 65 | int 66 | altitude_model_get_altitude(altitude_model_t* self, int time_into_flight, float* alt) { 67 | // TODO: this section needs some work to make it more flexible 68 | 69 | // time == 0 so setup initial altitude stuff 70 | if (time_into_flight == 0) { 71 | self->initial_alt = *alt; 72 | self->burst_time = (self->burst_altitude - self->initial_alt) / self->ascent_rate; 73 | } 74 | 75 | // If we are not doing a descending mode sim then start going up 76 | // at out ascent rate. The ascent rate is constant to a good approximation. 77 | if (self->descent_mode == DESCENT_MODE_NORMAL) 78 | if (time_into_flight <= self->burst_time) { 79 | *alt = self->initial_alt + time_into_flight*self->ascent_rate; 80 | return 1; 81 | } 82 | 83 | // Descent - just assume its at terminal velocity (which varies with altitude) 84 | // this is a pretty darn good approximation for high-ish drag e.g. under parachute 85 | // still converges to T.V. quickly (i.e. less than a minute) for low drag. 86 | // terminal velocity = -drag_coeff/sqrt(get_density(*alt)); 87 | *alt += TIMESTEP * -self->drag_coeff/sqrt(get_density(*alt)); 88 | 89 | /* 90 | // Rob's method - is this just freefall until we reach terminal velocity? 91 | vertical_speed = -drag_coeff/sqrt(get_density(*alt)); 92 | // TODO: inaccurate if still accelerating 93 | if (vertical_speed < previous_vertical_speed - G*TIMESTEP) // still accelerating 94 | vertical_speed = previous_vertical_speed - G*TIMESTEP; 95 | if(vertical_speed > previous_vertical_speed + G*TIMESTEP) // still accelerating 96 | vertical_speed = previous_vertical_speed + G*TIMESTEP; 97 | previous_vertical_speed = vertical_speed; 98 | *alt += TIMESTEP * vertical_speed; 99 | */ 100 | 101 | if (*alt <= 0) 102 | return 0; 103 | 104 | return 1; 105 | 106 | } 107 | 108 | float get_density(float altitude) { 109 | 110 | float temp = 0.f, pressure = 0.f; 111 | 112 | if (altitude > 25000) { 113 | temp = -131.21 + 0.00299 * altitude; 114 | pressure = 2.488*pow((temp+273.1)/216.6,-11.388); 115 | } 116 | if (altitude <=25000 && altitude > 11000) { 117 | temp = -56.46; 118 | pressure = 22.65 * exp(1.73-0.000157*altitude); 119 | } 120 | if (altitude <=11000) { 121 | temp = 15.04 - 0.00649 * altitude; 122 | pressure = 101.29 * pow((temp + 273.1)/288.08,5.256); 123 | } 124 | 125 | return pressure/(0.2869*(temp+273.1)); 126 | } 127 | -------------------------------------------------------------------------------- /predict/ajax.php: -------------------------------------------------------------------------------- 1 | counting('habhub.predictor.php.get_csv'); 25 | break; 26 | 27 | case "JSONexists": 28 | $uuid = $_GET['uuid']; 29 | if(file_exists(PREDS_PATH . $uuid . "/" . PROGRESS_JSON)) { 30 | echo true; 31 | } else { 32 | echo false; 33 | } 34 | break; 35 | 36 | case "locationSave": 37 | $lat = $_POST['req_lat']; 38 | $lon = $_POST['req_lon']; 39 | $alt = $_POST['req_alt']; 40 | $locname = $_POST['req_name']; 41 | if ( $locname == '' || !LOCATION_SAVE_ENABLE ) { 42 | echo "false"; 43 | return; 44 | } 45 | $str = "Latitude: " . $lat . "\n" . 46 | "Longitude: " . $lon . "\n" . 47 | "Altitude: " . $alt . "\n" . 48 | "Name: " . $locname . "\n"; 49 | $headers = "From: ". ADMIN_EMAIL ."\r\nReply-To:blackhole@hexoc.com\r\nX-Mailer: PHP/".phpversion(); 50 | if ( mail(ADMIN_EMAIL, "Location Request Save", $str, $headers) ) { 51 | echo "true"; 52 | } else { 53 | echo "false"; 54 | } 55 | break; 56 | 57 | case "getModelByUUID": 58 | $uuid = ( isset($_GET['uuid']) ? $_GET['uuid'] : false ); 59 | if( !$uuid ) die ("No uuid given to getModelByUUID"); 60 | // make a new model 61 | $pred_model = array(); 62 | if ( !file_exists(PREDS_PATH . $uuid . "/" . SCENARIO_FILE ) ) { 63 | $pred_model['valid'] = false; 64 | $stats->counting('habhub.predictor.php.couldnt_get_by_uuid'); 65 | } else { 66 | // populate the array, JSON encode it and return 67 | $pred_model = parse_ini_file(PREDS_PATH . $uuid . "/" . SCENARIO_FILE); 68 | if ( verifyModel($pred_model, $software_available) ){ 69 | $pred_model['valid'] = true; 70 | } else { 71 | $pred_model['valid'] = false; 72 | } 73 | $pred_model['uuid'] = $uuid; 74 | $stats->counting('habhub.predictor.php.got_by_uuid'); 75 | } 76 | echo json_encode($pred_model); 77 | break; 78 | 79 | case "submitForm": 80 | $pred_model = array(); 81 | $json_return = array(); 82 | $json_return['valid'] = "false"; 83 | 84 | // Make sure we have a submitted form 85 | if ( isset($_POST['submit'])) { 86 | // First, make a model from the form data 87 | if ( !$pred_model = createModel($_POST)) { 88 | $json_return['error'] = "Server couldn't make a model from the form 89 | data"; 90 | echo json_encode($json_return); 91 | $stats->counting('habhub.predictor.php.form_error'); 92 | break; 93 | } 94 | 95 | // If that worked, make sure the model is valid 96 | $verify_dump = verifyModel($pred_model, $software_available); 97 | if ( !$verify_dump['valid'] ) { 98 | $json_return['error'] = $verify_dump['msg']; 99 | echo json_encode($json_return); 100 | $stats->counting('habhub.predictor.php.invalid_model'); 101 | break; 102 | } 103 | 104 | // If we have a valid model, try and make a UUID 105 | if ( !$pred_model['uuid'] = makesha1hash($pred_model) ) { 106 | $json_return['error'] = "Couldn't make the SHA1 hash"; 107 | echo json_encode($json_return); 108 | $stats->counting('habhub.predictor.php.unhashable'); 109 | break; 110 | } 111 | 112 | // If all of the above worked, let's run the prediction 113 | runPred($pred_model); 114 | $json_return['valid'] = "true"; 115 | $json_return['uuid'] = $pred_model['uuid']; 116 | $json_return['timestamp'] = $pred_model['timestamp']; 117 | $stats->counting('habhub.predictor.php.prediction_run'); 118 | 119 | } else { 120 | $json_return['error'] = "The form submit function was called without 121 | any data"; 122 | $stats->counting('habhub.predictor.php.no_form_data'); 123 | } 124 | 125 | echo json_encode($json_return); 126 | break; 127 | 128 | default: 129 | echo "Couldn't interpret 'action' variable"; 130 | break; 131 | 132 | } 133 | 134 | ?> 135 | -------------------------------------------------------------------------------- /predict/js/jquery/jquery.tipsy.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $.fn.tipsy = function(options) { 3 | 4 | options = $.extend({}, $.fn.tipsy.defaults, options); 5 | 6 | return this.each(function() { 7 | 8 | var opts = $.fn.tipsy.elementOptions(this, options); 9 | 10 | $(this).hover(function() { 11 | 12 | $.data(this, 'cancel.tipsy', true); 13 | 14 | var tip = $.data(this, 'active.tipsy'); 15 | if (!tip) { 16 | tip = $('
'); 17 | tip.css({position: 'absolute', zIndex: 100000}); 18 | $.data(this, 'active.tipsy', tip); 19 | } 20 | 21 | if ($(this).attr('title') || typeof($(this).attr('original-title')) != 'string') { 22 | $(this).attr('original-title', $(this).attr('title') || '').removeAttr('title'); 23 | } 24 | 25 | var title; 26 | if (typeof opts.title == 'string') { 27 | title = $(this).attr(opts.title == 'title' ? 'original-title' : opts.title); 28 | } else if (typeof opts.title == 'function') { 29 | title = opts.title.call(this); 30 | } 31 | 32 | tip.find('.tipsy-inner')[opts.html ? 'html' : 'text'](title || opts.fallback); 33 | 34 | var pos = $.extend({}, $(this).offset(), {width: this.offsetWidth, height: this.offsetHeight}); 35 | tip.get(0).className = 'tipsy'; // reset classname in case of dynamic gravity 36 | tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body); 37 | var actualWidth = tip[0].offsetWidth, actualHeight = tip[0].offsetHeight; 38 | var gravity = (typeof opts.gravity == 'function') ? opts.gravity.call(this) : opts.gravity; 39 | 40 | switch (gravity.charAt(0)) { 41 | case 'n': 42 | tip.css({top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-north'); 43 | break; 44 | case 's': 45 | tip.css({top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-south'); 46 | break; 47 | case 'e': 48 | tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}).addClass('tipsy-east'); 49 | break; 50 | case 'w': 51 | tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}).addClass('tipsy-west'); 52 | break; 53 | } 54 | 55 | if (opts.fade) { 56 | tip.css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: 0.8}); 57 | } else { 58 | tip.css({visibility: 'visible'}); 59 | } 60 | 61 | }, function() { 62 | $.data(this, 'cancel.tipsy', false); 63 | var self = this; 64 | setTimeout(function() { 65 | if ($.data(this, 'cancel.tipsy')) return; 66 | var tip = $.data(self, 'active.tipsy'); 67 | if (opts.fade) { 68 | tip.stop().fadeOut(function() { $(this).remove(); }); 69 | } else { 70 | tip.remove(); 71 | } 72 | }, 100); 73 | 74 | }); 75 | 76 | }); 77 | 78 | }; 79 | 80 | // Overwrite this method to provide options on a per-element basis. 81 | // For example, you could store the gravity in a 'tipsy-gravity' attribute: 82 | // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' }); 83 | // (remember - do not modify 'options' in place!) 84 | $.fn.tipsy.elementOptions = function(ele, options) { 85 | return $.metadata ? $.extend({}, options, $(ele).metadata()) : options; 86 | }; 87 | 88 | $.fn.tipsy.defaults = { 89 | fade: false, 90 | fallback: '', 91 | gravity: 'n', 92 | html: false, 93 | title: 'title' 94 | }; 95 | 96 | $.fn.tipsy.autoNS = function() { 97 | return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n'; 98 | }; 99 | 100 | $.fn.tipsy.autoWE = function() { 101 | return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w'; 102 | }; 103 | 104 | })(jQuery); 105 | -------------------------------------------------------------------------------- /predict/js/pred/pred-cookie.js: -------------------------------------------------------------------------------- 1 | /* 2 | * CUSF Landing Prediction Version 2 3 | * Jon Sowman 2010 4 | * jon@hexoc.com 5 | * http://www.hexoc.com 6 | * 7 | * http://github.com/jonsowman/cusf-standalone-predictor 8 | * 9 | * This file contains all the cookie-related functions for the landing 10 | * predictor 11 | * 12 | */ 13 | 14 | function saveLocationToCookie() { 15 | // Get the variables from the form 16 | var req_lat = $("#req_lat").val(); 17 | var req_lon = $("#req_lon").val(); 18 | var req_alt = $("#req_alt").val(); 19 | var req_name = $("#req_name").val(); 20 | var cookie_name = "cusf_predictor"; 21 | var locations_limit = 5; 22 | var name_limit = 20; 23 | 24 | // Check the length of the name 25 | if ( req_name.length > name_limit ) { 26 | req_name = req_name.substr(0, name_limit); 27 | } 28 | 29 | // Now let's init the cookie 30 | $.Jookie.Initialise(cookie_name, 99999999); 31 | if ( !$.Jookie.Get(cookie_name, "idx") ) { 32 | $.Jookie.Set(cookie_name, "idx", 0); 33 | var idx = 0; 34 | } else { 35 | var idx = $.Jookie.Get(cookie_name, "idx"); 36 | } 37 | 38 | if ( $.Jookie.Get(cookie_name, "idx") >= locations_limit ) { 39 | $("#location_save").fadeOut(); 40 | throwError("You may only save " + locations_limit 41 | + " locations - please delete some."); 42 | } else { 43 | // Find the next free index we can use 44 | var i=1; 45 | while ( $.Jookie.Get(cookie_name, i+"_name") && i<=locations_limit ) { 46 | i++; 47 | } 48 | 49 | // We will use this idx for the next location 50 | $.Jookie.Set(cookie_name, i+"_lat", req_lat); 51 | $.Jookie.Set(cookie_name, i+"_lon", req_lon); 52 | $.Jookie.Set(cookie_name, i+"_alt", req_alt); 53 | $.Jookie.Set(cookie_name, i+"_name", req_name); 54 | 55 | // Increase the index 56 | idx++; 57 | $.Jookie.Set(cookie_name, "idx", idx); 58 | 59 | // Close dialog and let the user know it worked 60 | $("#location_save").hide(); 61 | appendDebug("Successfully saved the location to cookie " + cookie_name); 62 | } 63 | 64 | } 65 | 66 | // For when the user clicks the "Custom" link for Launch Site 67 | // Construct and display a table of their custom saved locations stored 68 | // in a cookie, and display it 69 | function constructCookieLocationsTable(cookie_name) { 70 | var t = ""; 71 | t += ""; 72 | 73 | $.Jookie.Initialise(cookie_name, 99999999); 74 | if ( !$.Jookie.Get(cookie_name, "idx") || $.Jookie.Get(cookie_name, "idx") == 0 ) { 75 | throwError("You haven't saved any locations yet. Please click Save Location to do so."); 76 | return false; 77 | } else { 78 | idx = $.Jookie.Get(cookie_name, "idx"); 79 | t += ""; 80 | var i=1; 81 | var j=0; 82 | while ( j"; 90 | t += ""; 91 | j++; 92 | } 93 | i++; 94 | } 95 | t += "
NameUseDelete
"; 86 | t += "Use"; 87 | t += ""; 88 | t += "Delete"; 89 | t += "
"; 96 | $("#locations_table").html(t); 97 | return true; 98 | } 99 | } 100 | 101 | // Given a cookie name and an location index, fill the launch card fields 102 | // before hiding the Custom locations window and plotting the new launch 103 | // site 104 | function setCookieLatLng(cookie_name, idx) { 105 | var req_lat = $.Jookie.Get(cookie_name, idx+"_lat"); 106 | var req_lon= $.Jookie.Get(cookie_name, idx+"_lon"); 107 | var req_alt = $.Jookie.Get(cookie_name, idx+"_alt"); 108 | $("#lat").val(req_lat); 109 | $("#lon").val(req_lon); 110 | $("#initial_alt").val(req_alt); 111 | // Now hide the window 112 | $("#location_save_local").fadeOut(); 113 | SetSiteOther(); 114 | plotClick(); 115 | } 116 | 117 | // Given a cookie name and a location index, delete the custom location that 118 | // is in the cookie 119 | function deleteCookieLocation(cookie_name, idx) { 120 | // Delete the location in the cookie 121 | $.Jookie.Unset(cookie_name, idx+"_lat"); 122 | $.Jookie.Unset(cookie_name, idx+"_lon"); 123 | $.Jookie.Unset(cookie_name, idx+"_alt"); 124 | $.Jookie.Unset(cookie_name, idx+"_name"); 125 | // Decrease quantity in cookie by one 126 | var qty = $.Jookie.Get(cookie_name, "idx"); 127 | qty -= 1; 128 | $.Jookie.Set(cookie_name, "idx", qty); 129 | // Now hide the window 130 | $("#location_save_local").fadeOut(); 131 | } 132 | 133 | -------------------------------------------------------------------------------- /predict/kml.php: -------------------------------------------------------------------------------- 1 | '); 37 | $kml[] = ''; 38 | $kml[] = ''; 39 | $kml[] = 'Flight Path'; 40 | $kml[] = 'Site: ' . $scenario['latitude'] . ', ' 41 | . $scenario['longitude'] . ' at ' . $scenario['hour'] . ':' . $scenario['minute'] 42 | . ' on ' . $scenario['day'] . '/' . $scenario['month'] . '/' . $scenario['year'] 43 | . ']]>'; 44 | $kml[] = ''; 53 | 54 | $kml[] = ''; 55 | $kml[] = 'Flight path'; 56 | $kml[] = 'Ascent rate: ' . $scenario['ascent-rate'] . 'm/s, descent rate: ' 57 | . $scenario['descent-rate'] . 'm/s with burst at ' . $scenario['burst-altitude'] 58 | . 'm.' . ''; 59 | $kml[] = '#yellowPoly'; 60 | $kml[] = ''; 61 | $kml[] = '1'; 62 | $kml[] = '1'; 63 | $kml[] = 'absolute'; 64 | $kml[] = ''; 65 | 66 | // now print the lat/long/alt from the CSV 67 | // GE is retarded and wants its KML in the format LON,LAT,ALT - wtf 68 | 69 | $launch_lat; 70 | $launch_lon; 71 | $launch_time; 72 | 73 | $burst_lat; 74 | $burst_lon; 75 | $burst_time; 76 | 77 | $land_lat; 78 | $land_lon; 79 | $land_time; 80 | 81 | $max_alt = -10; 82 | 83 | $fh = fopen($flight_csv, "r") or die("Could not open file"); 84 | $i=0; 85 | while (($data = fgetcsv($fh)) !== FALSE) { 86 | $num = count($data); 87 | if ( $num < 4 ) die("Invalid XML"); 88 | $time = $data[0]; 89 | $lat = $data[1]; 90 | $lon = $data[2]; 91 | $alt = $data[3]; 92 | 93 | if ( $i == 0 ) { 94 | $launch_lat = $lat; 95 | $launch_lon = $lon; 96 | $launch_time = $time; 97 | } 98 | 99 | // see if the current point is above the last 100 | if ( $alt > $max_alt ) { 101 | $max_alt = $alt; 102 | $burst_lat = $lat; 103 | $burst_lon = $lon; 104 | $burst_time = $time; 105 | } 106 | 107 | // update this on every interation 108 | $land_lat = $lat; 109 | $land_lon = $lon; 110 | $land_time = $time; 111 | 112 | // add the formatted line to the KML array 113 | $kml[] = $lon . "," . $lat . "," . $alt; 114 | $i++; 115 | } 116 | 117 | $kml[] = ''; 118 | $kml[] = ''; 119 | 120 | // Make the launch marker 121 | $kml[] = ''; 122 | $kml[] = 'Balloon Launch'; 123 | $kml[] = 'Balloon launch at ' . $launch_lat . ', ' . $launch_lon . ' at ' 124 | . date("H:i d/m/Y", $launch_time) . '.'; 125 | $kml[] = '' . $launch_lon . ',' . $launch_lat . ',0' . ''; 126 | $kml[] = ''; 127 | 128 | // Make the burst marker 129 | $kml[] = ''; 130 | $kml[] = 'Balloon Burst'; 131 | $kml[] = 'Balloon burst at ' . $burst_lat . ', ' . $burst_lon . ' at ' 132 | . date("H:i d/m/Y", $burst_time) . ' with altitude ' . $max_alt . 'm.'; 133 | $kml[] = '' . $burst_lon . ',' . $burst_lat . ',' . $max_alt . ''; 134 | $kml[] = ''; 135 | 136 | // Make the land marker 137 | $kml[] = ''; 138 | $kml[] = 'Predicted Balloon Landing'; 139 | $kml[] = 'Balloon landing at ' . $land_lat . ', ' . $land_lon . ' at ' 140 | . date("H:i d/m/Y", $land_time) . '.'; 141 | $kml[] = '' . $land_lon . ',' . $land_lat . ',0' . ''; 142 | $kml[] = ''; 143 | 144 | $kml[] = ''; 145 | 146 | $kmlOut = join($kml, "\n"); 147 | header("Content-type: application/vnd.google-earth.kml+xml"); 148 | header("Content-Disposition: attachment; filename=".$uuid.".kml"); 149 | echo $kmlOut; 150 | 151 | ?> 152 | -------------------------------------------------------------------------------- /predict/css/pred.css: -------------------------------------------------------------------------------- 1 | /* 2 | * CUSF Landing Prediction Version 2 3 | * http://www.cuspaceflight.co.uk 4 | * 5 | * Jon Sowman 2010 6 | * jon@hexoc.com 7 | * http://www.hexoc.com 8 | * 9 | * http://github.com/jonsowman/cusf-standalone-predictor 10 | * 11 | */ 12 | 13 | /* Basic font-size and family. */ 14 | html { 15 | font-family: sans-serif; 16 | font-size: 12px; 17 | } 18 | 19 | body { 20 | margin: 0; padding: 0; 21 | background-color: #000000; 22 | } 23 | 24 | a { text-decoration: underline; color: #333; cursor: pointer; } 25 | 26 | /* The whoppping map in the centre */ 27 | #map_canvas { 28 | width: 100%; 29 | height: 100%; 30 | } 31 | 32 | #status_message{ 33 | position: absolute; 34 | left: 50%; 35 | top: 50%; 36 | width: 400px; 37 | height: 100px; 38 | margin-left: -200px; 39 | margin-top: -50px; 40 | padding: 1em; 41 | text-align: center; 42 | display: none; 43 | } 44 | 45 | #error_window { 46 | position: absolute; 47 | left: 50%; 48 | top: 50%; 49 | width: 400px; 50 | margin-left: -200px; 51 | margin-top: -50px; 52 | padding: 1em; 53 | text-align: center; 54 | display: none; 55 | } 56 | 57 | #debuginfo { 58 | white-space: pre; 59 | } 60 | 61 | #burst-calc-wrapper { 62 | position: absolute; 63 | left: 50%; 64 | top: 50%; 65 | width: 400px; 66 | margin-left: -200px; 67 | margin-top: -200px; 68 | padding: 1em; 69 | text-align: center; 70 | display: none; 71 | } 72 | 73 | #notam-settings-wrapper { 74 | position: absolute; 75 | left: 50%; 76 | top: 50%; 77 | width: 400px; 78 | margin-left: -200px; 79 | margin-top: -200px; 80 | padding: 1em; 81 | text-align: center; 82 | display: none; 83 | } 84 | 85 | /* 86 | #burst-calc { 87 | display: none; 88 | padding: 5px; 89 | text-align: center; 90 | } 91 | */ 92 | 93 | #burst-calc-constants { 94 | display: none; 95 | } 96 | 97 | #location_save { 98 | position: absolute; 99 | left: 50%; 100 | top: 50%; 101 | width: 300px; 102 | margin-left: -150px; 103 | margin-top: -120px; 104 | padding: 1em; 105 | text-align: center; 106 | display: none; 107 | } 108 | 109 | #location_save_local { 110 | position: absolute; 111 | left: 50%; 112 | top: 50%; 113 | width: 300px; 114 | margin-left: -150px; 115 | margin-top: -120px; 116 | padding: 1em; 117 | text-align: center; 118 | display: none; 119 | } 120 | 121 | .box { 122 | position: absolute; 123 | background-color: white; 124 | border: 1px solid #0070a3; 125 | padding: 0.5em; 126 | } 127 | 128 | .box { 129 | background-attachment:initial; 130 | background-clip:initial; 131 | background-color:rgb(255, 255, 255); 132 | background-color:rgba(255, 255, 255, 0.746094); 133 | background-image:initial; 134 | background-origin:initial; 135 | background-position:initial initial; 136 | background-repeat:initial initial; 137 | } 138 | 139 | .box * { 140 | font-size: 12px; 141 | } 142 | 143 | .box table { 144 | margin: 0.25em auto; 145 | white-space: nowrap; 146 | } 147 | 148 | .box h1 { 149 | text-align: center; 150 | margin: 0em 0em 0.5em 0em; 151 | } 152 | 153 | .box p { 154 | margin: 0 auto; 155 | margin-bottom: 0.25em; 156 | width: 25em; 157 | } 158 | 159 | #trail_table { 160 | right: 0; bottom: 0; 161 | border-right: none; 162 | border-bottom: none; 163 | display: none; 164 | } 165 | 166 | #scenario_template { 167 | width: 400px; 168 | left: 0; bottom: 0; 169 | border-left: none; 170 | border-bottom: none; 171 | max-height: 400px; 172 | overflow: scroll; 173 | display: none; 174 | } 175 | 176 | #input_form { 177 | overflow: hidden; 178 | width: 400px; 179 | padding: 5px 5px 0px 5px; 180 | bottom: 0; right: 0; 181 | max-height: 500px; 182 | } 183 | 184 | #launch-card { 185 | width: 100%; 186 | padding: 5px; 187 | } 188 | 189 | #launch-card tr { 190 | width: 50%; 191 | } 192 | 193 | #scenario_info { 194 | position: absolute; 195 | top: 25px; 196 | right: 0px; 197 | width: 400px; 198 | text-align:center; 199 | padding: 5px; 200 | line-height: 130%; 201 | } 202 | 203 | /* about window is a jqueryui dialog */ 204 | #about_window { 205 | display: none; 206 | } 207 | 208 | /* the vehicle_button buttons from spacenear */ 209 | .control_buttons { 210 | border: solid #aed0ea 1px; 211 | background-color: #deedf7; 212 | padding: 3px; 213 | margin-top: 1em; 214 | color: #888; 215 | -webkit-user-select:none; 216 | -moz-user-select:none; 217 | cursor: default; 218 | line-height: 220%; 219 | font-size: 10px; 220 | } 221 | 222 | .control_button { 223 | font-weight: bold; 224 | color: #5c9dc2; 225 | cursor: pointer; 226 | text-decoration: none; 227 | } 228 | 229 | a.control_button:hover { 230 | color: #0070a3; 231 | } 232 | 233 | img.handle { 234 | position: relative; 235 | float:left; 236 | top:0px; 237 | left:0px; 238 | z-index:100; 239 | } 240 | 241 | td.right-td { 242 | text-align: right; 243 | } 244 | 245 | td.spacer { 246 | width: 1em; 247 | } 248 | 249 | table#trails { 250 | border-collapse: collapse; 251 | } 252 | 253 | table#trails td, table#trails th { 254 | border: 1px solid #6666ff; 255 | padding: 0.1em 0.5em; 256 | text-align: center; 257 | } 258 | 259 | table#trails th { 260 | background-color: #6666ff; 261 | color: white; 262 | } 263 | -------------------------------------------------------------------------------- /predict/js/utils/date.jsport.js: -------------------------------------------------------------------------------- 1 | Date.prototype.monthNames = new Array( 2 | "January", "February", "March", "April", 3 | "May", "June", "July", "August", 4 | "September", "October", "November", "December" 5 | ); 6 | 7 | 8 | Date.prototype.dayNames = new Array( 9 | "Sunday", "Monday", "Tuesday", "Wednesday", 10 | "Thursday", "Friday", "Saturday" 11 | ); 12 | 13 | 14 | Date.prototype.format = function (formatStr) { 15 | var heap = formatStr.split(""); 16 | var resHeap = new Array(heap.length); 17 | var escapeChar = "\\"; // you can change this to something different, but 18 | // don't use a character that has a formatting meaning, 19 | // unless you want to disable it's functionality 20 | 21 | // go through array and extract identifiers from its fields 22 | for (var i = 0; i < heap.length; i++) { 23 | switch(heap[i]) { 24 | case escapeChar: 25 | resHeap[i] = heap[i+1]; 26 | i++; 27 | break; 28 | 29 | case "a": // "am" or "pm" 30 | var temp = this.getHours(); 31 | resHeap[i] = (temp < 12) ? "am" : "pm"; 32 | break; 33 | 34 | case "A": // "AM" or "PM" 35 | var temp = this.getHours(); 36 | resHeap[i] = (temp < 12) ? "AM" : "PM"; 37 | break; 38 | 39 | case "d": // day of the month, 2 digits with leading zeros; i.e. "01" to "31" 40 | var temp = String(this.getDate()); 41 | resHeap[i] = (temp.length > 1) ? temp : "0" + temp; 42 | break; 43 | 44 | case "D": // day of the week, textual, 3 letters; i.e. "Fri" 45 | var temp = this.dayNames[this.getDay()]; 46 | resHeap[i] = temp.substring(0, 3); 47 | break; 48 | 49 | case "F": // month, textual, long; i.e. "January" 50 | resHeap[i] = this.monthNames[this.getMonth()]; 51 | break; 52 | 53 | case "g": // hour, 12-hour format without leading zeros; i.e. "1" to "12" 54 | var temp = this.getHours(); 55 | resHeap[i] = (temp <= 12) ? temp : (temp - 12); 56 | break; 57 | 58 | case "G": // hour, 24-hour format without leading zeros; i.e. "0" to "23" 59 | resHeap[i] = String(this.getHours()); 60 | break; 61 | 62 | case "h": // hour, 12-hour format; i.e. "01" to "12" 63 | var temp = String(this.getHours()); 64 | temp = (temp <= 12) ? temp : (temp - 12); 65 | resHeap[i] = (temp.length > 1) ? temp : "0" + temp; 66 | break; 67 | 68 | case "H": // hour, 24-hour format; i.e. "00" to "23" 69 | var temp = String(this.getHours()); 70 | resHeap[i] = (temp.length > 1) ? temp : "0" + temp; 71 | break; 72 | 73 | case "i": // minutes; i.e. "00" to "59" 74 | var temp = String(this.getMinutes()); 75 | resHeap[i] = (temp.length > 1) ? temp : "0" + temp; 76 | break; 77 | 78 | case "I": // "1" if Daylight Savings Time, "0" otherwise. Works only on the northern hemisphere 79 | var firstDay = new Date(this.getFullYear(), 0, 1); 80 | resHeap[i] = (this.getTimezoneOffset() != firstDay.getTimezoneOffset()) ? (1) : (0); 81 | break; 82 | 83 | case "J": // day of the month without leading zeros; i.e. "1" to "31" 84 | resHeap[i] = this.getDate(); 85 | break; 86 | 87 | case "l": // day of the week, textual, long; i.e. "Friday" 88 | resHeap[i] = this.dayNames[this.getDay()]; 89 | break; 90 | 91 | case "L": // boolean for whether it is a leap year; i.e. "0" or "1" 92 | resHeap[i] = (this.getFullYear() % 4) ? false : true; 93 | break; 94 | 95 | case "m": // month; i.e. "01" to "12" 96 | var temp = String(this.getMonth() + 1); 97 | resHeap[i] = (temp.length > 1) ? temp : "0" + temp; 98 | break; 99 | 100 | case "M": // month, textual, 3 letters; i.e. "Jan" 101 | resHeap[i] = this.monthNames[this.getMonth()]; 102 | break; 103 | 104 | case "n": // month without leading zeros; i.e. "1" to "12" 105 | resHeap[i] = this.getMonth() + 1; 106 | break; 107 | 108 | case "O": // Difference to Greenwich time in hours; i.e. "+0200" 109 | var minZone = this.getTimezoneOffset(); 110 | var mins = minZone % 60; 111 | var hour = String(((minZone - mins) / 60) * -1); 112 | 113 | if (hour.charAt(0) != "-") { 114 | hour = "+" + hour; 115 | } 116 | 117 | hour = (hour.length == 3) ? (hour) : (hour.replace(/([+-])(\d)/, "$1" + 0 + "$2")); 118 | resHeap[i] = hour + mins + "0"; 119 | break; 120 | 121 | case "r": // RFC 822 formatted date; e.g. "Thu, 21 Dec 2000 16:01:07 +0200" 122 | var dayName = this.dayNames[this.getDay()].substr(0, 3); 123 | var monthName = this.monthNames[this.getMonth()].substr(0, 3); 124 | resHeap[i] = dayName + ", " + this.getDate() + " " + monthName + this.format(" Y H:i:s O"); 125 | break; 126 | 127 | case "s": // seconds; i.e. "00" to "59" 128 | var temp = String(this.getSeconds()); 129 | resHeap[i] = (temp.length > 1) ? temp : "0" + temp; 130 | break; 131 | 132 | case "S": // English ordinal suffix for the day of the month, 2 characters; i.e. "st", "nd", "rd" or "th" 133 | var temp = this.getDate(); 134 | var suffixes = ["st", "nd", "rd"]; 135 | var suffix = ""; 136 | 137 | if (temp >= 11 && temp <= 13) { 138 | resHeap[i] = "th"; 139 | } else { 140 | resHeap[i] = (suffix = suffixes[String(temp).substr(-1) - 1]) ? (suffix) : ("th"); 141 | } 142 | break; 143 | 144 | 145 | case "t": // number of days in the given month; i.e. "28" to "31" 146 | resHeap[i] = this.getDay(); 147 | break; 148 | 149 | /* 150 | * T: Not implemented 151 | */ 152 | 153 | case "U": // seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 154 | // remember that this does not return milisecs! 155 | resHeap[i] = Math.floor(this.getTime() / 1000); 156 | break; 157 | 158 | case "w": // day of the week, numeric, i.e. "0" (Sunday) to "6" (Saturday) 159 | resHeap[i] = this.getDay(); 160 | break; 161 | 162 | 163 | case "W": // ISO-8601 week number of year, weeks starting on Monday 164 | var startOfYear = new Date(this.getFullYear(), 0, 1, 0, 0, 0, 0); 165 | var firstDay = startOfYear.getDay() - 1; 166 | 167 | if (firstDay < 0) { 168 | firstDay = 6; 169 | } 170 | 171 | var firstMonday = Date.UTC(this.getFullYear(), 0, 8 - firstDay); 172 | var thisDay = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate()); 173 | resHeap[i] = Math.floor((thisDay - firstMonday) / (1000 * 60 * 60 * 24 * 7)) + 2; 174 | break; 175 | 176 | case "y": // year, 2 digits; i.e. "99" 177 | resHeap[i] = String(this.getFullYear()).substring(2); 178 | break; 179 | 180 | case "Y": // year, 4 digits; i.e. "1999" 181 | resHeap[i] = this.getFullYear(); 182 | break; 183 | 184 | case "z": // day of the year; i.e. "0" to "365" 185 | var firstDay = Date.UTC(this.getFullYear(), 0, 0); 186 | var thisDay = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate()); 187 | resHeap[i] = Math.floor((thisDay - firstDay) / (1000 * 60 * 60 * 24)); 188 | break; 189 | 190 | case "Z": // timezone offset in seconds (i.e. "-43200" to "43200"). 191 | resHeap[i] = this.getTimezoneOffset() * 60; 192 | break; 193 | 194 | default: 195 | resHeap[i] = heap[i]; 196 | } 197 | } 198 | 199 | // return joined array 200 | return resHeap.join(""); 201 | } 202 | -------------------------------------------------------------------------------- /pred_src/ini/dictionary.h: -------------------------------------------------------------------------------- 1 | 2 | /*-------------------------------------------------------------------------*/ 3 | /** 4 | @file dictionary.h 5 | @author N. Devillard 6 | @date Sep 2007 7 | @version $Revision: 1.12 $ 8 | @brief Implements a dictionary for string variables. 9 | 10 | This module implements a simple dictionary object, i.e. a list 11 | of string/string associations. This object is useful to store e.g. 12 | informations retrieved from a configuration file (ini files). 13 | */ 14 | /*--------------------------------------------------------------------------*/ 15 | 16 | /* 17 | $Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp $ 18 | $Author: ndevilla $ 19 | $Date: 2007-11-23 21:37:00 $ 20 | $Revision: 1.12 $ 21 | */ 22 | 23 | #ifndef _DICTIONARY_H_ 24 | #define _DICTIONARY_H_ 25 | 26 | /*--------------------------------------------------------------------------- 27 | Includes 28 | ---------------------------------------------------------------------------*/ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | /*--------------------------------------------------------------------------- 36 | New types 37 | ---------------------------------------------------------------------------*/ 38 | 39 | 40 | /*-------------------------------------------------------------------------*/ 41 | /** 42 | @brief Dictionary object 43 | 44 | This object contains a list of string/string associations. Each 45 | association is identified by a unique string key. Looking up values 46 | in the dictionary is speeded up by the use of a (hopefully collision-free) 47 | hash function. 48 | */ 49 | /*-------------------------------------------------------------------------*/ 50 | typedef struct _dictionary_ { 51 | int n ; /** Number of entries in dictionary */ 52 | int size ; /** Storage size */ 53 | char ** val ; /** List of string values */ 54 | char ** key ; /** List of string keys */ 55 | unsigned * hash ; /** List of hash values for keys */ 56 | } dictionary ; 57 | 58 | 59 | /*--------------------------------------------------------------------------- 60 | Function prototypes 61 | ---------------------------------------------------------------------------*/ 62 | 63 | /*-------------------------------------------------------------------------*/ 64 | /** 65 | @brief Compute the hash key for a string. 66 | @param key Character string to use for key. 67 | @return 1 unsigned int on at least 32 bits. 68 | 69 | This hash function has been taken from an Article in Dr Dobbs Journal. 70 | This is normally a collision-free function, distributing keys evenly. 71 | The key is stored anyway in the struct so that collision can be avoided 72 | by comparing the key itself in last resort. 73 | */ 74 | /*--------------------------------------------------------------------------*/ 75 | unsigned dictionary_hash(char * key); 76 | 77 | /*-------------------------------------------------------------------------*/ 78 | /** 79 | @brief Create a new dictionary object. 80 | @param size Optional initial size of the dictionary. 81 | @return 1 newly allocated dictionary objet. 82 | 83 | This function allocates a new dictionary object of given size and returns 84 | it. If you do not know in advance (roughly) the number of entries in the 85 | dictionary, give size=0. 86 | */ 87 | /*--------------------------------------------------------------------------*/ 88 | dictionary * dictionary_new(int size); 89 | 90 | /*-------------------------------------------------------------------------*/ 91 | /** 92 | @brief Delete a dictionary object 93 | @param d dictionary object to deallocate. 94 | @return void 95 | 96 | Deallocate a dictionary object and all memory associated to it. 97 | */ 98 | /*--------------------------------------------------------------------------*/ 99 | void dictionary_del(dictionary * vd); 100 | 101 | /*-------------------------------------------------------------------------*/ 102 | /** 103 | @brief Get a value from a dictionary. 104 | @param d dictionary object to search. 105 | @param key Key to look for in the dictionary. 106 | @param def Default value to return if key not found. 107 | @return 1 pointer to internally allocated character string. 108 | 109 | This function locates a key in a dictionary and returns a pointer to its 110 | value, or the passed 'def' pointer if no such key can be found in 111 | dictionary. The returned character pointer points to data internal to the 112 | dictionary object, you should not try to free it or modify it. 113 | */ 114 | /*--------------------------------------------------------------------------*/ 115 | char * dictionary_get(dictionary * d, char * key, char * def); 116 | 117 | 118 | /*-------------------------------------------------------------------------*/ 119 | /** 120 | @brief Set a value in a dictionary. 121 | @param d dictionary object to modify. 122 | @param key Key to modify or add. 123 | @param val Value to add. 124 | @return int 0 if Ok, anything else otherwise 125 | 126 | If the given key is found in the dictionary, the associated value is 127 | replaced by the provided one. If the key cannot be found in the 128 | dictionary, it is added to it. 129 | 130 | It is Ok to provide a NULL value for val, but NULL values for the dictionary 131 | or the key are considered as errors: the function will return immediately 132 | in such a case. 133 | 134 | Notice that if you dictionary_set a variable to NULL, a call to 135 | dictionary_get will return a NULL value: the variable will be found, and 136 | its value (NULL) is returned. In other words, setting the variable 137 | content to NULL is equivalent to deleting the variable from the 138 | dictionary. It is not possible (in this implementation) to have a key in 139 | the dictionary without value. 140 | 141 | This function returns non-zero in case of failure. 142 | */ 143 | /*--------------------------------------------------------------------------*/ 144 | int dictionary_set(dictionary * vd, char * key, char * val); 145 | 146 | /*-------------------------------------------------------------------------*/ 147 | /** 148 | @brief Delete a key in a dictionary 149 | @param d dictionary object to modify. 150 | @param key Key to remove. 151 | @return void 152 | 153 | This function deletes a key in a dictionary. Nothing is done if the 154 | key cannot be found. 155 | */ 156 | /*--------------------------------------------------------------------------*/ 157 | void dictionary_unset(dictionary * d, char * key); 158 | 159 | 160 | /*-------------------------------------------------------------------------*/ 161 | /** 162 | @brief Dump a dictionary to an opened file pointer. 163 | @param d Dictionary to dump 164 | @param f Opened file pointer. 165 | @return void 166 | 167 | Dumps a dictionary onto an opened file pointer. Key pairs are printed out 168 | as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as 169 | output file pointers. 170 | */ 171 | /*--------------------------------------------------------------------------*/ 172 | void dictionary_dump(dictionary * d, FILE * out); 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /predict/js/pred/pred-event.js: -------------------------------------------------------------------------------- 1 | /* 2 | * CUSF Landing Prediction Version 2 3 | * Jon Sowman 2010 4 | * jon@hexoc.com 5 | * http://www.hexoc.com 6 | * 7 | * http://github.com/jonsowman/cusf-standalone-predictor 8 | * 9 | * This file contains the event handlers used in the predictor, which are 10 | * numerous. They are divided into functions that setup handlers for each 11 | * part of the predictor, and a calling function 12 | * 13 | */ 14 | 15 | function setupEventHandlers() { 16 | EH_LaunchCard(); 17 | EH_BurstCalc(); 18 | EH_NOTAMSettings(); 19 | EH_ScenarioInfo(); 20 | EH_LocationSave(); 21 | 22 | // Tipsylink tooltip class activation 23 | $(".tipsyLink").tipsy({fade: true}); 24 | 25 | // Add the onmove event handler to the map canvas 26 | google.maps.event.addListener(map, 'mousemove', function(event) { 27 | showMousePos(event.latLng); 28 | }); 29 | } 30 | 31 | function EH_BurstCalc() { 32 | // Activate the "use burst calc" links 33 | $("#burst-calc-show").click(function() { 34 | $("#burst-calc-wrapper").show(); 35 | }); 36 | $("#burst-calc-show").hover( 37 | function() { 38 | $("#ascent,#burst").css("background-color", "#AACCFF"); 39 | }, 40 | function() { 41 | $("#ascent,#burst").css("background-color", "#FFFFFF"); 42 | }); 43 | $("#burst-calc-use").click(function() { 44 | // Write the ascent rate and burst altitude to the launch card 45 | $("#ascent").val($("#ar").html()); 46 | $("#burst").val($("#ba").html()); 47 | $("#burst-calc-wrapper").hide(); 48 | }); 49 | $("#burst-calc-close").click(function() { 50 | // Close the burst calc without doing anything 51 | $("#burst-calc-wrapper").hide(); 52 | $("#modelForm").show(); 53 | }); 54 | $("#burst-calc-advanced-show").click(function() { 55 | // Show the burst calculator constants 56 | // We use a callback function to fade in the new content to make 57 | // sure the old content has gone, in order to create a smooth effect 58 | $("#burst-calc").fadeOut('fast', function() { 59 | $("#burst-calc-constants").fadeIn(); 60 | }); 61 | }); 62 | $("#burst-calc-advanced-hide").click(function() { 63 | // Show the burst calculator constants 64 | $("#burst-calc-constants").fadeOut('fast', function() { 65 | $("#burst-calc").fadeIn(); 66 | }); 67 | }); 68 | } 69 | 70 | function EH_NOTAMSettings() { 71 | // Activate the checkbox 72 | $("#notam-display").click(function() { 73 | if (document.modelForm.notams.checked){ 74 | if (kmlLayer == null) kmlLayer = new google.maps.KmlLayer('http://www.habhub.org/kml_testing/notam_and_restrict.kml', {preserveViewport: true}); 75 | kmlLayer.setMap(map); 76 | } 77 | else { 78 | kmlLayer.setMap(null); 79 | } 80 | }); 81 | // Activate the "notam settings" links 82 | $("#notam-settings-show").click(function() { 83 | $("#notam-settings-wrapper").show(); 84 | }); 85 | $("#notam-settings-close").click(function() { 86 | // Close the notam settings doing anything 87 | $("#notam-settings-wrapper").hide(); 88 | $("#modelForm").show(); 89 | }); 90 | } 91 | 92 | function EH_LaunchCard() { 93 | // Attach form submit event handler to Run Prediction button 94 | $("#modelForm").ajaxForm({ 95 | url: 'ajax.php?action=submitForm', 96 | type: 'POST', 97 | dataType: 'json', 98 | success: function(data) { 99 | if ( data.valid == "false" ) { 100 | // If something went wrong, write the error messages to 101 | // the debug window 102 | appendDebug("The server rejected the submitted form data:"); 103 | appendDebug(data.error); 104 | // And throw an error window to alert the user of what happened 105 | throwError("The server rejected the submitted form data: \n" 106 | + data.error); 107 | resetGUI(); 108 | } else if ( data.valid == "true" ) { 109 | predSub(); 110 | appendDebug("The server accepted the form data"); 111 | // Update the global current_uuid variable 112 | current_uuid = data.uuid; 113 | appendDebug("The server gave us uuid:
" + current_uuid); 114 | appendDebug("Starting to poll for progress JSON"); 115 | handlePred(current_uuid); 116 | } else { 117 | appendDebug("data.valid was not a recognised state: " 118 | + data.valid); 119 | } 120 | } 121 | }); 122 | // Activate the "Set with Map" link 123 | $("#setWithClick").click(function() { 124 | setLatLonByClick(true); 125 | }); 126 | $("#setWithClick,#req_open").hover( 127 | function() { 128 | $("#lat,#lon").css("background-color", "#AACCFF"); 129 | }, 130 | function() { 131 | $("#lat,#lon").css("background-color", "#FFFFFF"); 132 | }); 133 | // Launch card parameter onchange event handlers 134 | $("#lat").change(function() { 135 | plotClick(); 136 | }); 137 | $("#lon").change(function() { 138 | plotClick(); 139 | }); 140 | 141 | $("#delta_lat").change(function() { 142 | drawDeltaSquare(map); 143 | }); 144 | $("#delta_lon").change(function() { 145 | drawDeltaSquare(map); 146 | }); 147 | $("#site").change(function() { 148 | changeLaunchSite(); 149 | }); 150 | } 151 | 152 | function EH_ScenarioInfo() { 153 | // Controls in the Scenario Information window 154 | $("#showHideDebug").click(function() { 155 | toggleWindow("scenario_template", "showHideDebug", "Show Debug", "Hide Debug"); 156 | }); 157 | $("#showHideDebug_status").click(function() { 158 | toggleWindow("scenario_template", "showHideDebug", "Show Debug", "Hide Debug"); 159 | }); 160 | $("#showHideForm").click(function() { 161 | toggleWindow("input_form", "showHideForm", "Show Launch Card", 162 | "Hide Launch Card"); 163 | }); 164 | $("#closeErrorWindow").click(function() { 165 | $("#error_window").fadeOut(); 166 | }); 167 | 168 | $("#about_window_show").click(function() { 169 | $("#about_window").dialog({ 170 | modal:true, 171 | width:600, 172 | buttons: { 173 | Close: function() { 174 | $(this).dialog('close'); 175 | } 176 | } 177 | }); 178 | }); 179 | } 180 | 181 | function EH_LocationSave() { 182 | // Location saving to cookies event handlers 183 | $("#req_sub_btn").click(function() { 184 | saveLocationToCookie(); 185 | }); 186 | $("#cookieLocations").click(function() { 187 | appendDebug("User requested locally saved launch sites"); 188 | if ( constructCookieLocationsTable("cusf_predictor") ) { 189 | $("#location_save_local").fadeIn(); 190 | } 191 | }); 192 | $("#req_open").click(function() { 193 | var lat = $("#lat").val(); 194 | var lon = $("#lon").val(); 195 | $("#req_lat").val(lat); 196 | $("#req_lon").val(lon); 197 | $("#req_alt").val($("#initial_alt").val()); 198 | appendDebug("Trying to reverse geo-code the launch point"); 199 | rvGeocode(lat, lon, "req_name"); 200 | $("#location_save").fadeIn(); 201 | }) 202 | $("#req_close").click(function() { 203 | $("#location_save").fadeOut(); 204 | }); 205 | $("#locations_close").click(function() { 206 | $("#location_save_local").fadeOut(); 207 | }); 208 | } 209 | -------------------------------------------------------------------------------- /predict/includes/functions.inc.php: -------------------------------------------------------------------------------- 1 | $value ){ 56 | $sha1str .= $idx . "=" . $value . ","; 57 | } 58 | $uuid = sha1($sha1str); 59 | return $uuid; 60 | } 61 | 62 | // Check that the model that we built was valid 63 | // This involves sanity checking all the parameters 64 | function verifyModel( $pred_model, $software_available ) { 65 | // Check that we have not been passed an empty model 66 | if( !isset( $pred_model ) ) return false; 67 | 68 | // We will return an array of information to the calling function 69 | $return_array; 70 | $return_array['valid'] = true; 71 | 72 | // Iterate though the scenario parameters 73 | foreach( $pred_model as $idx => $value ) { 74 | if ( $idx == "software" ) { 75 | if ( !in_array( $value, $software_available ) ) { 76 | $return_array['valid'] = false; 77 | $return_array['msg'] = "The model asked for software that 78 | does not exist on this server"; 79 | } 80 | } 81 | else if ( !is_numeric( $value ) ) { 82 | $return_array['valid'] = false; 83 | $return_array['msg'] = "A value that should have been numeric 84 | did not validate as such"; 85 | } 86 | 87 | if ( $idx == "delta_lat" || $idx == "delta_lon" ) { 88 | if ( $value < 1 || $value > 10 ) { 89 | $return_array['valid'] = false; 90 | $return_array['msg'] = "The latitude or longitude deltas 91 | were outside the allowed range on this server"; 92 | } 93 | } else if ( $idx == "delta_time" ) { 94 | if ( $value < 5 || $value > 24) { 95 | $return_array['valid'] = false; 96 | $return_array['msg'] = "The time delta was 97 | outside the allowed range on this server"; 98 | } 99 | } else if ( $idx == "asc" || $idx == "des" ) { 100 | if ( $value <= 0 ) { 101 | $return_array['valid'] = false; 102 | $return_array['msg'] = "The ascent and descent rates cannot 103 | be zero or negative"; 104 | } 105 | } 106 | } 107 | 108 | // Now check that the timestamp is within range 109 | if ( !isset($pred_model['timestamp']) ) { 110 | $return_array['valid'] = false; 111 | $return_array['msg'] = "Launch time missing"; 112 | } else if ( $pred_model['timestamp'] > (time() + 180*3600) ) { 113 | // More than 180 hours into future 114 | $return_array['valid'] = false; 115 | $return_array['msg'] = "A prediction cannot be run for a time that is 116 | more than 180 hours in the future"; 117 | } else if ( $pred_model['timestamp'] < time() ) { 118 | // Can't run predictions in the past 119 | $return_array['valid'] = false; 120 | $return_array['msg'] = "A prediction cannot be run for a time that 121 | is in the past"; 122 | } 123 | 124 | // Return true if all went okay 125 | return $return_array; 126 | } 127 | 128 | // Run the prediction given a prediction model 129 | function runPred($pred_model) { 130 | // Check if this is a re-run 131 | if ( !file_exists(PREDS_PATH . $pred_model['uuid'] . "/" . SCENARIO_FILE) ) 132 | { 133 | // If not, make a new directory and scenario file 134 | makePredDir($pred_model) or die ("Couldn't create the scenario dir"); 135 | makeINI($pred_model); 136 | } 137 | 138 | // If using GFS HD, then append --hd to the exec string 139 | if ( $pred_model['software'] == "gfs_hd" ) $use_hd ="--hd "; 140 | else $use_hd = ""; 141 | 142 | $predictor_lat = number_format($pred_model['lat'], 0); 143 | $predictor_lon = number_format($pred_model['lon'], 0); 144 | 145 | $log = PREDS_PATH . $pred_model['uuid'] . "/" . LOG_FILE; 146 | $sh = ROOT . "/predict.py --cd=" . ROOT . " --fork --alarm --redirect=predict/$log -v --latdelta=" 147 | .$pred_model['delta_lat']." --londelta=".$pred_model['delta_lon'] 148 | ." -p1 -f".$pred_model['delta_time']." -t ".$pred_model['timestamp'] 149 | ." --lat=".$predictor_lat." --lon=".$predictor_lon." " . $use_hd 150 | . $pred_model['uuid']; 151 | if (defined("PYTHON")) 152 | $sh = PYTHON . " " . $sh; 153 | 154 | file_put_contents($log, "Command: " . $sh . "\n"); 155 | shell_exec($sh); 156 | } 157 | 158 | // Use PHP's mkdir() to create a directory for the prediction data using 159 | // the UUID for the scenario 160 | function makePredDir($pred_model) { 161 | //make sure we use the uuid from model 162 | if ( mkdir( PREDS_PATH . $pred_model['uuid'] ) ) { 163 | return true; 164 | } else { 165 | return false; 166 | } 167 | } 168 | 169 | // Write the scenario model parameters to an INI file that can be read by 170 | // the predictor binary 171 | function makeINI($pred_model) { // makes an ini file 172 | $fh = fopen(PREDS_PATH . $pred_model['uuid'] . "/" . SCENARIO_FILE, "w"); //write 173 | 174 | // Hacky (and hopefully temporary) fix for issue #77 175 | $hacked_lon = $pred_model['lon']; 176 | if ($hacked_lon < 0) $hacked_lon += 360.0; 177 | 178 | $w_string = "[launch-site]\nlatitude = " . $pred_model['lat'] . "\naltitude = " . $pred_model['alt'] . "\n"; 179 | $w_string .= "longitude = " . $hacked_lon . "\n[atmosphere]\nwind-error = "; 180 | $w_string .= $pred_model['wind_error'] . "\n[altitude-model]\nascent-rate = " . $pred_model['asc'] . "\n"; 181 | $w_string .= "descent-rate = " . $pred_model['des'] . "\nburst-altitude = "; 182 | $w_string .= $pred_model['burst'] . "\n[launch-time]\nhour = " . $pred_model['hour'] . "\n"; 183 | $w_string .= "month = " . $pred_model['month'] . "\nsecond = " . $pred_model['sec'] . "\n"; 184 | $w_string .= "year = " . $pred_model['year'] . "\nday = " . $pred_model['day'] . "\nminute = "; 185 | $w_string .= $pred_model['min'] . "\n"; 186 | // add our predictor stuff 187 | $w_string .= "[predictor]\nlat-delta = " . $pred_model['delta_lat'] . "\n"; 188 | $w_string .= "time-delta = " . $pred_model['delta_time'] . "\n"; 189 | $w_string .= "lon-delta = " . $pred_model['delta_lon'] . "\nsoftware = "; 190 | $w_string .= $pred_model['software'] . "\n"; 191 | 192 | fwrite($fh, $w_string); 193 | fclose($fh); 194 | } 195 | 196 | // Given a UUID, return the prediction scenario model 197 | function getModelByUUID($uuid) { 198 | if ( file_exists( PREDS_PATH . $uuid . "/" . SCENARIO_FILE ) ) { 199 | $pred_model = parse_ini_file(PREDS_PATH . $uuid . "/" . SCENARIO_FILE); 200 | return $pred_model; 201 | } else { 202 | return false; 203 | } 204 | } 205 | 206 | ?> 207 | -------------------------------------------------------------------------------- /pred_src/run_model.c: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rob Anderson 6 | // Modified by Fergus Noble 7 | // 8 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 9 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 10 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 11 | // PARTICULAR PURPOSE. 12 | // -------------------------------------------------------------- 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "wind/wind_file.h" 20 | #include "util/random.h" 21 | #include "run_model.h" 22 | #include "pred.h" 23 | #include "altitude.h" 24 | 25 | extern int verbosity; 26 | 27 | #define RADIUS_OF_EARTH 6371009.f 28 | 29 | typedef struct model_state_s model_state_t; 30 | struct model_state_s 31 | { 32 | float lat; 33 | float lng; 34 | float alt; 35 | altitude_model_t *alt_model; 36 | double loglik; 37 | }; 38 | 39 | // Get the distance (in metres) of one degree of latitude and one degree of 40 | // longitude. This varys with height (not much grant you). 41 | static void 42 | _get_frame(float lat, float lng, float alt, 43 | float *d_dlat, float *d_dlng) 44 | { 45 | float theta, r; 46 | 47 | theta = 2.f * M_PI * (90.f - lat) / 360.f; 48 | r = RADIUS_OF_EARTH + alt; 49 | 50 | // See the differentiation section of 51 | // http://en.wikipedia.org/wiki/Spherical_coordinate_system 52 | 53 | // d/dv = d/dlat = -d/dtheta 54 | *d_dlat = (2.f * M_PI) * r / 360.f; 55 | 56 | // d/du = d/dlong = d/dphi 57 | *d_dlng = (2.f * M_PI) * r * sinf(theta) / 360.f; 58 | } 59 | 60 | static int 61 | _advance_one_timestep(wind_file_cache_t* cache, 62 | unsigned long delta_t, 63 | unsigned long timestamp, unsigned long initial_timestamp, 64 | unsigned int n_states, model_state_t* states, 65 | float rmserror) 66 | { 67 | unsigned int i; 68 | 69 | for(i=0; ialt_model, 77 | timestamp - initial_timestamp, &state->alt)) 78 | return 0; // alt < 0; finished 79 | 80 | if(!get_wind(cache, state->lat, state->lng, state->alt, timestamp, 81 | &wind_v, &wind_u, &wind_var)) 82 | return -1; // error 83 | 84 | _get_frame(state->lat, state->lng, state->alt, &ddlat, &ddlng); 85 | 86 | // NOTE: it this really the right thing to be doing? - think about what 87 | // happens near the poles 88 | 89 | wind_var += rmserror * rmserror; 90 | 91 | assert(wind_var >= 0.f); 92 | 93 | //fprintf(stderr, "U: %f +/- %f, V: %f +/- %f\n", 94 | // wind_u, sqrtf(wind_u_var), 95 | // wind_v, sqrtf(wind_v_var)); 96 | 97 | u_samp = random_sample_normal(wind_u, wind_var, &u_lik); 98 | v_samp = random_sample_normal(wind_v, wind_var, &v_lik); 99 | 100 | //u_samp = wind_u; 101 | //v_samp = wind_v; 102 | 103 | state->lat += v_samp * delta_t / ddlat; 104 | state->lng += u_samp * delta_t / ddlng; 105 | 106 | state->loglik += (double)(u_lik + v_lik); 107 | } 108 | 109 | return 1; // OK, and continue 110 | } 111 | 112 | static int _state_compare_rev(const void* a, const void *b) 113 | { 114 | model_state_t* sa = (model_state_t*)a; 115 | model_state_t* sb = (model_state_t*)b; 116 | 117 | // this returns a value s.t. the states will be sorted so that 118 | // the maximum likelihood state is at position 0. 119 | return sb->loglik - sa->loglik; 120 | } 121 | 122 | int run_model(wind_file_cache_t* cache, altitude_model_t* alt_model, 123 | float initial_lat, float initial_lng, float initial_alt, 124 | long int initial_timestamp, float rmswinderror) 125 | { 126 | model_state_t* states; 127 | const unsigned int n_states = 1; 128 | unsigned int i; 129 | 130 | states = (model_state_t*) malloc( sizeof(model_state_t) * n_states ); 131 | 132 | for(i=0; ialt = initial_alt; 137 | state->lat = initial_lat; 138 | state->lng = initial_lng; 139 | state->alt_model = alt_model; 140 | state->loglik = 0.f; 141 | } 142 | 143 | long int timestamp = initial_timestamp; 144 | 145 | int log_counter = 0; // only write position to output files every LOG_DECIMATE timesteps 146 | int r, return_code = 1; 147 | 148 | while(1) 149 | { 150 | r = _advance_one_timestep(cache, TIMESTEP, timestamp, initial_timestamp, 151 | n_states, states, rmswinderror); 152 | if (r == -1) // error getting wind. Save prediction, but emit error messages 153 | return_code = 0; 154 | 155 | if (r != 1) // 1 = continue 156 | break; 157 | 158 | // Sort the array of models in order of log likelihood. 159 | qsort(states, n_states, sizeof(model_state_t), _state_compare_rev); 160 | 161 | // write the maximum likelihood state out. 162 | if (log_counter == LOG_DECIMATE) { 163 | write_position(states[0].lat, states[0].lng, states[0].alt, timestamp); 164 | log_counter = 0; 165 | } 166 | 167 | log_counter++; 168 | timestamp += TIMESTEP; 169 | } 170 | 171 | for(i=0; ilat, state->lng, state->alt, timestamp); 175 | } 176 | 177 | fprintf(stderr, "INFO: Final maximum log lik: %f (=%f)\n", 178 | states[0].loglik, exp(states[0].loglik)); 179 | 180 | free(states); 181 | 182 | return return_code; 183 | } 184 | 185 | int get_wind(wind_file_cache_t* cache, float lat, float lng, float alt, long int timestamp, 186 | float* wind_v, float* wind_u, float *wind_var) { 187 | int i, s; 188 | float lambda, wu_l, wv_l, wu_h, wv_h; 189 | float wuvar_l, wvvar_l, wuvar_h, wvvar_h; 190 | wind_file_cache_entry_t* found_entries[] = { NULL, NULL }; 191 | wind_file_t* found_files[] = { NULL, NULL }; 192 | unsigned int earlier_ts, later_ts; 193 | 194 | // look for a wind file which matches this latitude and longitude... 195 | wind_file_cache_find_entry(cache, lat, lng, timestamp, 196 | &(found_entries[0]), &(found_entries[1])); 197 | 198 | if(!found_entries[0] || !found_entries[1]) { 199 | fprintf(stderr, "ERROR: Do not have wind data for this (lat, lon, alt, time).\n"); 200 | return 0; 201 | } 202 | 203 | if(!wind_file_cache_entry_contains_point(found_entries[0], lat, lng) || 204 | !wind_file_cache_entry_contains_point(found_entries[1], lat, lng)) 205 | { 206 | fprintf(stderr, "ERROR: Could not locate appropriate wind data tile for location " 207 | "lat=%f, lon=%f.\n", lat, lng); 208 | return 0; 209 | } 210 | 211 | // Look in the cache for the files we need. 212 | for(i=0; i<2; ++i) 213 | { 214 | found_files[i] = wind_file_cache_entry_file(found_entries[i]); 215 | } 216 | 217 | earlier_ts = wind_file_cache_entry_timestamp(found_entries[0]); 218 | later_ts = wind_file_cache_entry_timestamp(found_entries[1]); 219 | 220 | if(earlier_ts > timestamp || later_ts < timestamp) 221 | { 222 | fprintf(stderr, "Error: found_entries have bad times.\n"); 223 | return 0; 224 | } 225 | 226 | if(earlier_ts != later_ts) 227 | lambda = ((float)timestamp - (float)earlier_ts) / 228 | ((float)later_ts - (float)earlier_ts); 229 | else 230 | lambda = 0.5f; 231 | 232 | s = wind_file_get_wind(found_files[0], lat, lng, alt, &wu_l, &wv_l, &wuvar_l, &wvvar_l); 233 | if (s == 0) return 0; // hard error 234 | s = wind_file_get_wind(found_files[1], lat, lng, alt, &wu_h, &wv_h, &wuvar_h, &wvvar_h); 235 | if (s == 0) return 0; 236 | 237 | *wind_u = lambda * wu_h + (1.f-lambda) * wu_l; 238 | *wind_v = lambda * wv_h + (1.f-lambda) * wv_l; 239 | 240 | // flatten the u and v variances into a single mean variance for the 241 | // magnitude. 242 | *wind_var = 0.5f * (wuvar_h + wuvar_l + wvvar_h + wvvar_l); 243 | 244 | return 1; 245 | } 246 | 247 | // vim:sw=4:ts=4:et:cindent 248 | -------------------------------------------------------------------------------- /predict/js/pred/pred-map.js: -------------------------------------------------------------------------------- 1 | /* 2 | * CUSF Landing Prediction Version 2 3 | * Jon Sowman 2010 4 | * jon@hexoc.com 5 | * http://www.hexoc.com 6 | * 7 | * http://github.com/jonsowman/cusf-standalone-predictor 8 | * 9 | * This file contains all of the prediction javascript functions 10 | * that are explicitly related to Google Map manipulation 11 | * 12 | */ 13 | 14 | // Initialise the map canvas with (lat, long, zoom) 15 | function initMap(centre_lat, centre_lon, zoom_level) { 16 | // Make the map and set center 17 | var latlng = new google.maps.LatLng(centre_lat, centre_lon); 18 | var myOptions = { 19 | zoom: zoom_level, 20 | scaleControl: true, 21 | scaleControlOptions: { position: google.maps.ControlPosition.BOTTOM_LEFT } , 22 | mapTypeId: google.maps.MapTypeId.TERRAIN, 23 | center: latlng 24 | }; 25 | map = new google.maps.Map(document.getElementById("map_canvas"), myOptions); 26 | } 27 | 28 | // Enable or disable user control of the map canvas, including scrolling, 29 | // zooming and clicking 30 | function enableMap(map, state) { 31 | if ( state != false && state != true) { 32 | appendDebug("Unrecognised map state"); 33 | } else if (state == false) { 34 | map.draggable = false; 35 | map.disableDoubleClickZoom = true; 36 | map.scrollwheel = false; 37 | map.navigationControl = false; 38 | } else if (state == true ) { 39 | map.draggable = true; 40 | map.disableDoubleClickZoom = false; 41 | map.scrollwheel = false; 42 | map.navigationControl = true; 43 | } 44 | } 45 | 46 | // This should be called on a "mousemove" event handler on the map canvas 47 | // and will update scenario information display 48 | function showMousePos(GLatLng) { 49 | var curr_lat = GLatLng.lat().toFixed(4); 50 | var curr_lon = GLatLng.lng().toFixed(4); 51 | $("#cursor_lat").html(curr_lat); 52 | $("#cursor_lon").html(curr_lon); 53 | // if we have a prediction displayed 54 | // show range from launch and land: 55 | if ( current_uuid != 0 && map_items['launch_marker'] != null ) { 56 | var launch_pt = map_items['launch_marker'].position; 57 | var land_pt = map_items['land_marker'].position; 58 | var range_launch = distHaversine(launch_pt, GLatLng, 1); 59 | var range_land = distHaversine(land_pt, GLatLng, 1); 60 | $("#cursor_pred_launchrange").html(range_launch); 61 | $("#cursor_pred_landrange").html(range_land); 62 | } 63 | 64 | } 65 | 66 | // Takes an array of points and the name of the gmap object and plots 67 | // the polygon onto the map 68 | function drawPolygon(points, gmap_object) { 69 | var newPoly = new google.maps.Polyline({ 70 | path: points, 71 | strokeColor: "#FF0000", 72 | strokeOpacity: 0.4, 73 | //fillColor: "#FFFFFF", 74 | //fillOpacity: 0, 75 | strokeWeight: 2 76 | }); 77 | map_items['delta_square'] = newPoly; 78 | newPoly.setMap(gmap_object); 79 | } 80 | 81 | // Read the latitude and longitude currently in the launch card and plot 82 | // a marker there with hover information 83 | function plotClick() { 84 | // Clear the old marker 85 | clearMapItems(); 86 | // Get the new values from the form 87 | click_lat = parseFloat($("#lat").val()); 88 | click_lon = parseFloat($("#lon").val()); 89 | // Make sure the data is valid before we try and do anything with it 90 | if ( isNaN(click_lat) || isNaN(click_lon) ) return; 91 | var click_pt = new google.maps.LatLng(click_lat, click_lon); 92 | clickMarker = new google.maps.Marker({ 93 | position: click_pt, 94 | map: map, 95 | icon: 'images/target-1-sm.png', 96 | title: 'Currently selected launch location (' + click_lat + ', ' 97 | + click_lon+')' 98 | }); 99 | map_items['clickMarker'] = clickMarker; 100 | // Redraw the delta square 101 | drawDeltaSquare(map); 102 | map.panTo(click_pt); 103 | map.setZoom(8); 104 | } 105 | 106 | // Uses the currently selected lat, lon and delta values in the launch 107 | // card to draw a square of the GFS data to be downloaded for the prediction 108 | function drawDeltaSquare(map) { 109 | // Clear the old delta square if it exists 110 | if ( map_items['delta_square'] ) map_items['delta_square'].setMap(null); 111 | // Get the values from the form 112 | var lat = Math.round(parseFloat($("#lat").val())); 113 | var lon = Math.round(parseFloat($("#lon").val())); 114 | var dlat = parseFloat($("#delta_lat").val()); 115 | var dlon = parseFloat($("#delta_lon").val()); 116 | // Construct a rectange of points 117 | var points = [ 118 | new google.maps.LatLng(lat+dlat, lon+dlon), 119 | new google.maps.LatLng(lat-dlat, lon+dlon), 120 | new google.maps.LatLng(lat-dlat, lon-dlon), 121 | new google.maps.LatLng(lat+dlat, lon-dlon), 122 | new google.maps.LatLng(lat+dlat, lon+dlon) 123 | ] 124 | // Draw this polygon onto the map canvas 125 | drawPolygon(points, map); 126 | } 127 | 128 | // Given a GLatLng object, write the latitude and longitude to the launch card 129 | function setFormLatLon(GLatLng) { 130 | appendDebug("Trying to set the form lat long"); 131 | $("#lat").val(GLatLng.lat().toFixed(4)); 132 | $("#lon").val(GLatLng.lng().toFixed(4)); 133 | // Remove the event handler so another click doesn't register 134 | setLatLonByClick(false); 135 | // Change the dropdown to read "other" 136 | SetSiteOther(); 137 | // Plot the new marker for launch location 138 | appendDebug("Plotting the new launch location marker"); 139 | plotClick(); 140 | } 141 | 142 | // Enable or disable an event handler which, when a mouse click is detected 143 | // on the map canvas, will write the coordinates of the clicked place to the 144 | // launch card 145 | function setLatLonByClick(state) { 146 | if ( state == true ) { 147 | // Check this listener doesn't already exist 148 | if (!clickListener) { 149 | appendDebug("Enabling the set with click listener"); 150 | clickListener = google.maps.event.addListener(map, 'click', function(event) { 151 | appendDebug("Got a click from user, setting values into form"); 152 | $("#error_window").fadeOut(); 153 | setFormLatLon(event.latLng); 154 | }); 155 | } 156 | // Tell the user what to do next 157 | throwError("Now click your desired launch location on the map"); 158 | } else if ( state == false ) { 159 | appendDebug("Removing the set with click listener"); 160 | google.maps.event.removeListener(clickListener); 161 | clickListener = null; 162 | } else { 163 | appendDebug("Unrecognised state for setLatLonByClick"); 164 | } 165 | } 166 | 167 | // An associative array exists globally containing all objects we have placed 168 | // onto the map canvas - this function clears all of them 169 | function clearMapItems() { 170 | $("#cursor_pred").hide(); 171 | if( getAssocSize(map_items) > 0 ) { 172 | appendDebug("Clearing previous map trace"); 173 | for( i in map_items ) { 174 | map_items[i].setMap(null); 175 | } 176 | } 177 | map_items = []; 178 | } 179 | 180 | // The Haversine formula to calculate the distance across the surface between 181 | // two points on the Earth 182 | distHaversine = function(p1, p2, precision) { 183 | var R = 6371; // earth's mean radius in km 184 | var dLat = rad(p2.lat() - p1.lat()); 185 | var dLong = rad(p2.lng() - p1.lng()); 186 | 187 | var a = Math.sin(dLat/2) * Math.sin(dLat/2) + 188 | Math.cos(rad(p1.lat())) * Math.cos(rad(p2.lat())) * Math.sin(dLong/2) * Math.sin(dLong/2); 189 | var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 190 | var d = R * c; 191 | if ( precision == null ) { 192 | return d.toFixed(3); 193 | } else { 194 | return d.toFixed(precision); 195 | } 196 | } 197 | 198 | // Given a latitude, longitude, and a field to write the result to, 199 | // find the name of the place using Google "reverse Geocode" API feature 200 | function rvGeocode(lat, lon, fillField) { 201 | var geocoder = new google.maps.Geocoder(); 202 | var latlng = new google.maps.LatLng(parseFloat(lat), parseFloat(lon)); 203 | var coded = "Unnamed"; 204 | geocoder.geocode({'latLng': latlng}, function(results, status) { 205 | if ( status == google.maps.GeocoderStatus.OK ) { 206 | // Successfully got rv-geocode information 207 | appendDebug("Got a good response from the geocode server"); 208 | coded = results[1].address_components[1].short_name; 209 | } else { 210 | appendDebug("The rv-geocode failed: " + status); 211 | } 212 | // Now write the value to the field 213 | $("#"+fillField+"").val(coded); 214 | }); 215 | } 216 | -------------------------------------------------------------------------------- /pred_src/util/gopt.c: -------------------------------------------------------------------------------- 1 | /* gopt.c version 8.1: tom.viza@gmail.com PUBLIC DOMAIN 2003-8 */ 2 | /* 3 | I, Tom Vajzovic, am the author of this software and its documentation and 4 | permanently abandon all copyright and other intellectual property rights in 5 | them, including the right to be identified as the author. 6 | 7 | I am fairly certain that this software does what the documentation says it 8 | does, but I cannot guarantee that it does, or that it does what you think it 9 | should, and I cannot guarantee that it will not have undesirable side effects. 10 | 11 | You are free to use, modify and distribute this software as you please, but 12 | you do so at your own risk. If you remove or hide this warning then you are 13 | responsible for any problems encountered by people that you make the software 14 | available to. 15 | 16 | Before modifying or distributing this software I ask that you would please 17 | read http://www.purposeful.co.uk/tfl/ 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "gopt.h" 24 | 25 | #ifdef USE_SYSEXITS 26 | #include 27 | #else 28 | #define EX_OSERR EXIT_FAILURE 29 | #define EX_USAGE EXIT_FAILURE 30 | #endif 31 | 32 | struct opt_spec_s { 33 | int key; 34 | int flags; 35 | const char *shorts; 36 | const char* const *longs; 37 | }; 38 | typedef struct opt_spec_s opt_spec_t; 39 | 40 | struct opt_s { 41 | int key; 42 | const char *arg; 43 | }; 44 | typedef struct opt_s opt_t; 45 | 46 | void *gopt_sort( int *argc, const char **argv, const void *opt_specs ){ 47 | void *opts; 48 | {{{ 49 | const char* const *arg_p= argv + 1; 50 | size_t opt_count= 1; 51 | for( ; *arg_p; ++arg_p ) { 52 | if( '-' == (*arg_p)[0] && (*arg_p)[1] ) { 53 | if( '-' == (*arg_p)[1] ) { 54 | if( (*arg_p)[2] ) { 55 | ++opt_count; 56 | } else { 57 | break; 58 | } 59 | } else { 60 | const opt_spec_t *opt_spec_p= opt_specs; 61 | for( ; opt_spec_p-> key; ++opt_spec_p ) 62 | if( strchr( opt_spec_p-> shorts, (*arg_p)[1] )){ 63 | opt_count+= opt_spec_p-> flags & GOPT_ARG ? 1 : strlen( (*arg_p) + 1 ); 64 | break; 65 | } 66 | } 67 | } 68 | } 69 | opts= malloc( opt_count * sizeof(opt_t) ); 70 | }}} 71 | { 72 | const char **arg_p= argv + 1; 73 | const char **next_operand= arg_p; 74 | opt_t *next_option= opts; 75 | 76 | if( ! opts ){ 77 | perror( argv[0] ); 78 | exit( EX_OSERR ); 79 | } 80 | for( ; *arg_p; ++arg_p ) 81 | if( '-' == (*arg_p)[0] && (*arg_p)[1] ) 82 | if( '-' == (*arg_p)[1] ) 83 | if( (*arg_p)[2] ) 84 | {{{ 85 | const opt_spec_t *opt_spec_p= opt_specs; 86 | const char* const *longs= opt_spec_p-> longs; 87 | next_option-> key= 0; 88 | while( *longs ){ 89 | const char *option_cp= (*arg_p) + 2; 90 | const char *name_cp= *longs; 91 | while( *option_cp && *option_cp == *name_cp ){ 92 | ++option_cp; 93 | ++name_cp; 94 | } 95 | if( '=' == *option_cp || !*option_cp ){ 96 | if( *name_cp ){ 97 | if( next_option-> key ){ 98 | fprintf( stderr, "%s: --%.*s: abbreviated option is ambiguous\n", argv[0], (int)( option_cp -( (*arg_p) + 2 )), (*arg_p) + 2 ); 99 | free( opts ); 100 | exit( EX_USAGE ); 101 | } 102 | next_option-> key= opt_spec_p-> key; 103 | } 104 | else { 105 | next_option-> key= opt_spec_p-> key; 106 | goto found_long; 107 | } 108 | } 109 | if( !*++longs ){ 110 | ++opt_spec_p; 111 | if( opt_spec_p-> key ) 112 | longs= opt_spec_p-> longs; 113 | } 114 | } 115 | if( ! next_option-> key ){ 116 | fprintf( stderr, "%s: --%.*s: unknown option\n", argv[0], (int)strcspn( (*arg_p) + 2, "=" ), (*arg_p) + 2 ); 117 | free( opts ); 118 | exit( EX_USAGE ); 119 | } 120 | for( opt_spec_p= opt_specs; opt_spec_p-> key != next_option-> key; ++opt_spec_p ); 121 | found_long: 122 | 123 | if( !( opt_spec_p-> flags & GOPT_REPEAT )){ 124 | const opt_t *opt_p= opts; 125 | for( ; opt_p != next_option; ++opt_p ) 126 | if( opt_p-> key == opt_spec_p-> key ){ 127 | fprintf( stderr, "%s: --%.*s: option may not be repeated (in any long or short form)\n", argv[0], (int)strcspn( (*arg_p) + 2, "=" ), (*arg_p) + 2 ); 128 | free( opts ); 129 | exit( EX_USAGE ); 130 | } 131 | } 132 | if( opt_spec_p-> flags & GOPT_ARG ){ 133 | next_option-> arg= strchr( (*arg_p) + 2, '=' ) + 1; 134 | if( (char*)0 + 1 == next_option-> arg ){ 135 | ++arg_p; 136 | if( !*arg_p || ('-' == (*arg_p)[0] && (*arg_p)[1]) ){ 137 | fprintf( stderr, "%s: --%s: option requires an option argument\n", argv[0], (*(arg_p-1)) + 2 ); 138 | free( opts ); 139 | exit( EX_USAGE ); 140 | } 141 | next_option-> arg= *arg_p; 142 | } 143 | } 144 | else { 145 | if( strchr( (*arg_p) + 2, '=' )){ 146 | fprintf( stderr, "%s: --%.*s: option may not take an option argument\n", argv[0], (int)strcspn( (*arg_p) + 2, "=" ), (*arg_p) + 2 ); 147 | free( opts ); 148 | exit( EX_USAGE ); 149 | } 150 | next_option-> arg= NULL; 151 | } 152 | ++next_option; 153 | }}} 154 | else { 155 | for( ++arg_p; *arg_p; ++arg_p ) 156 | *next_operand++= *arg_p; 157 | break; 158 | } 159 | else 160 | {{{ 161 | const char *short_opt= (*arg_p) + 1; 162 | for( ;*short_opt; ++short_opt ){ 163 | const opt_spec_t *opt_spec_p= opt_specs; 164 | 165 | for( ; opt_spec_p-> key; ++opt_spec_p ) 166 | if( strchr( opt_spec_p-> shorts, *short_opt )){ 167 | if( !( opt_spec_p-> flags & GOPT_REPEAT )){ 168 | const opt_t *opt_p= opts; 169 | for( ; opt_p != next_option; ++opt_p ) 170 | if( opt_p-> key == opt_spec_p-> key ){ 171 | fprintf( stderr, "%s: -%c: option may not be repeated (in any long or short form)\n", argv[0], *short_opt ); 172 | free( opts ); 173 | exit( EX_USAGE ); 174 | } 175 | } 176 | next_option-> key= opt_spec_p-> key; 177 | 178 | if( opt_spec_p-> flags & GOPT_ARG ){ 179 | if( short_opt[1] ) 180 | next_option-> arg= short_opt + 1; 181 | 182 | else { 183 | ++arg_p; 184 | if( !*arg_p || ('-' == (*arg_p)[0] && (*arg_p)[1]) ){ 185 | fprintf( stderr, "%s: -%c: option requires an option argument\n", argv[0], *short_opt ); 186 | free( opts ); 187 | exit( EX_USAGE ); 188 | } 189 | next_option-> arg= *arg_p; 190 | } 191 | ++next_option; 192 | goto break_2; 193 | } 194 | next_option-> arg= NULL; 195 | ++next_option; 196 | goto continue_2; 197 | } 198 | fprintf( stderr, "%s: -%c: unknown option\n", argv[0], *short_opt ); 199 | free( opts ); 200 | exit( EX_USAGE ); 201 | continue_2: ; 202 | } 203 | break_2: ; 204 | }}} 205 | else 206 | *next_operand++= *arg_p; 207 | 208 | next_option-> key= 0; 209 | *next_operand= NULL; 210 | *argc= next_operand - argv; 211 | } 212 | return opts; 213 | } 214 | 215 | size_t gopt( const void *vptr_opts, int key ){ 216 | const opt_t *opts= vptr_opts; 217 | size_t count= 0; 218 | for( ; opts-> key; ++opts ) 219 | count+= opts-> key == key; 220 | 221 | return count; 222 | } 223 | 224 | size_t gopt_arg( const void *vptr_opts, int key, const char **arg ){ 225 | const opt_t *opts= vptr_opts; 226 | size_t count= 0; 227 | 228 | for( ; opts-> key; ++opts ) 229 | if( opts-> key == key ){ 230 | if( ! count ) 231 | *arg= opts-> arg; 232 | ++count; 233 | } 234 | return count; 235 | } 236 | 237 | const char *gopt_arg_i( const void *vptr_opts, int key, size_t i ){ 238 | const opt_t *opts= vptr_opts; 239 | 240 | for( ; opts-> key; ++opts ) 241 | if( opts-> key == key ){ 242 | if( ! i ) 243 | return opts-> arg; 244 | --i; 245 | } 246 | return NULL; 247 | } 248 | 249 | size_t gopt_args( const void *vptr_opts, int key, const char **args, size_t args_len ){ 250 | const char **args_stop= args + args_len; 251 | const char **args_ptr= args; 252 | const opt_t *opts= vptr_opts; 253 | 254 | for( ; opts-> key; ++opts ) 255 | if( opts-> key == key ){ 256 | if( args_stop == args_ptr ) 257 | return args_len + gopt( opts, key ); 258 | 259 | *args_ptr++= opts-> arg; 260 | } 261 | if( args_stop != args_ptr ) 262 | *args_ptr= NULL; 263 | return args_ptr - args; 264 | } 265 | 266 | void gopt_free( void *vptr_opts ){ 267 | free( vptr_opts ); 268 | } 269 | -------------------------------------------------------------------------------- /pred_src/wind/wind_file_cache.c: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rich Wareham 6 | // 7 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 8 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 9 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 10 | // PARTICULAR PURPOSE. 11 | // -------------------------------------------------------------- 12 | 13 | #include "wind_file_cache.h" 14 | #include "wind_file.h" 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "../util/getline.h" 30 | 31 | extern int verbosity; 32 | 33 | struct wind_file_cache_entry_s 34 | { 35 | char *filepath; // Full path. 36 | unsigned long timestamp; // As POSIX timestamp. 37 | float lat, lon; // Window centre. 38 | float latrad, lonrad; // Window radius. 39 | wind_file_t *loaded_file; // Initially NULL. 40 | }; 41 | 42 | struct wind_file_cache_s 43 | { 44 | char *directory_name; 45 | unsigned int n_entries; 46 | struct wind_file_cache_entry_s **entries; // Matching directory entries. 47 | }; 48 | 49 | // Yuk! Needed to make use of scandir. Gotta love APIs designed in the 80s. 50 | static wind_file_cache_t* _scandir_current_cache; 51 | 52 | static int 53 | _parse_header(const char* filepath, 54 | float *lat, float *latrad, 55 | float *lon, float *lonrad, 56 | unsigned long* timestamp) 57 | { 58 | FILE* file; 59 | char* line; 60 | size_t line_len; 61 | 62 | // Can I open this file? 63 | file = fopen(filepath, "r"); 64 | if(!file) { 65 | // No, abort 66 | return 0; 67 | } 68 | 69 | // Look for first non-comment line. 70 | line = NULL; 71 | while((getline(&line, &line_len, file) >= 0) && (line[0] == '#')) 72 | { 73 | // line does not need to be free-ed since getline will realloc 74 | // it if it is too small. See getline(3). 75 | } 76 | 77 | if(feof(file)) 78 | { 79 | // Got to the end without finding non-comment. 80 | free(line); 81 | fclose(file); 82 | return 0; 83 | } 84 | 85 | 86 | // 'line' is first non-comment. Try to parse it. 87 | if(5 != sscanf(line, "%f,%f,%f,%f,%ld", lat, latrad, lon, lonrad, timestamp)) 88 | { 89 | // Failed to parse, it is invalid. 90 | free(line); 91 | fclose(file); 92 | return 0; 93 | } 94 | 95 | // File seems valid. 96 | free(line); 97 | fclose(file); 98 | 99 | return 1; 100 | } 101 | 102 | #ifdef __APPLE__ 103 | static int 104 | _file_filter(struct dirent *entry) 105 | #else 106 | static int 107 | _file_filter(const struct dirent *entry) 108 | #endif 109 | { 110 | int filepath_len; 111 | int rv; 112 | wind_file_cache_t* self = _scandir_current_cache; 113 | char* filepath = NULL; 114 | struct stat stat_buf; 115 | 116 | float lat, latrad, lon, lonrad; 117 | unsigned long timestamp; 118 | 119 | // This is using sprintf in C99 mode to create a buffer with 120 | // the full file path/ 121 | filepath_len = 1 + snprintf(NULL, 0, "%s/%s", self->directory_name, entry->d_name); 122 | filepath = (char*)malloc(filepath_len); 123 | snprintf(filepath, filepath_len, "%s/%s", self->directory_name, entry->d_name); 124 | 125 | // Stat the file. 126 | rv = stat(filepath, &stat_buf); 127 | if(rv < 0) 128 | { 129 | perror("Error scanning data dir"); 130 | free(filepath); 131 | return 0; 132 | } 133 | 134 | // Is this a regular file? 135 | if(!S_ISREG(stat_buf.st_mode)) 136 | { 137 | free(filepath); 138 | return 0; 139 | } 140 | 141 | // Can I parse out the header? 142 | if(!_parse_header(filepath, &lat, &latrad, &lon, &lonrad, ×tamp)) 143 | { 144 | free(filepath); 145 | return 0; 146 | } 147 | 148 | free(filepath); 149 | 150 | return 1; 151 | } 152 | 153 | wind_file_cache_t* 154 | wind_file_cache_new(const char *directory) 155 | { 156 | wind_file_cache_t* self; 157 | int rv, i; 158 | struct dirent **dir_entries; 159 | 160 | assert(directory); 161 | 162 | // Allocate memory for ourself 163 | self = (wind_file_cache_t*) malloc(sizeof(wind_file_cache_t)); 164 | self->n_entries = 0; 165 | self->directory_name = strdup(directory); 166 | 167 | if(verbosity > 0) 168 | fprintf(stderr, "INFO: Scanning directory '%s'.\n", directory); 169 | 170 | // Use scandir scan the directory looking for data files. 171 | _scandir_current_cache = self; // ew! 172 | rv = scandir(directory, &dir_entries, _file_filter, alphasort); 173 | if(rv < 0) { 174 | perror(NULL); 175 | wind_file_cache_free(self); 176 | return NULL; 177 | } 178 | if(verbosity > 0) 179 | fprintf(stderr, "INFO: Found %i data files.\n", rv); 180 | self->n_entries = rv; 181 | 182 | self->entries = (struct wind_file_cache_entry_s**)malloc(sizeof(struct wind_file_cache_entry_s*)*self->n_entries); 183 | for(i=0; in_entries; ++i) 184 | { 185 | struct dirent* entry = dir_entries[i]; 186 | int filepath_len, parse_rv; 187 | char* filepath = NULL; 188 | 189 | // allocate the entry. 190 | self->entries[i] = (struct wind_file_cache_entry_s*)malloc(sizeof(struct wind_file_cache_entry_s)); 191 | 192 | // This is using sprintf in C99 mode to create a buffer with 193 | // the full file path/ 194 | filepath_len = 1 + snprintf(NULL, 0, "%s/%s", self->directory_name, entry->d_name); 195 | filepath = (char*)malloc(filepath_len); 196 | snprintf(filepath, filepath_len, "%s/%s", self->directory_name, entry->d_name); 197 | 198 | // Fill in the file path. 199 | self->entries[i]->filepath = filepath; 200 | 201 | // Parse the file header 202 | parse_rv = _parse_header(filepath, 203 | &(self->entries[i]->lat), &(self->entries[i]->latrad), 204 | &(self->entries[i]->lon), &(self->entries[i]->lonrad), 205 | &(self->entries[i]->timestamp)); 206 | if(!parse_rv) 207 | { 208 | fprintf(stderr, "WARN: Hmm... some files appear to have " 209 | "changed under me!"); 210 | } 211 | 212 | if(verbosity > 1) { 213 | fprintf(stderr, "INFO: Found %s.\n", filepath); 214 | fprintf(stderr, "INFO: - Covers window (lat, long) = " 215 | "(%f +/-%f, %f +/-%f).\n", 216 | self->entries[i]->lat, self->entries[i]->latrad, 217 | self->entries[i]->lon, self->entries[i]->lonrad); 218 | } 219 | 220 | // initially, no file is loaded. 221 | self->entries[i]->loaded_file = NULL; 222 | 223 | // finished with this entry 224 | free(dir_entries[i]); 225 | } 226 | // finished with the dir entries. 227 | free(dir_entries); 228 | 229 | return self; 230 | } 231 | 232 | void 233 | wind_file_cache_free(wind_file_cache_t *cache) 234 | { 235 | if(!cache) 236 | return; 237 | 238 | free(cache->directory_name); 239 | 240 | if(cache->n_entries > 0) 241 | { 242 | unsigned int i; 243 | for(i=0; in_entries; ++i) 244 | { 245 | free(cache->entries[i]->filepath); 246 | free(cache->entries[i]); 247 | cache->entries[i] = NULL; 248 | } 249 | free(cache->entries); 250 | } 251 | 252 | free(cache); 253 | } 254 | 255 | static float 256 | _lon_dist(float a, float b) 257 | { 258 | float d1 = fabs(a-b); 259 | float d2 = 360.f - d1; 260 | return (d1 < d2) ? d1 : d2; 261 | } 262 | 263 | int 264 | wind_file_cache_entry_contains_point(wind_file_cache_entry_t* entry, float lat, float lon) 265 | { 266 | if(!entry) 267 | return 0; 268 | 269 | if(fabs(entry->lat - lat) > entry->latrad) 270 | return 0; 271 | 272 | if(_lon_dist(entry->lon, lon) > entry->lonrad) 273 | return 0; 274 | 275 | return 1; 276 | } 277 | 278 | void 279 | wind_file_cache_find_entry(wind_file_cache_t *cache, 280 | float lat, float lon, unsigned long timestamp, 281 | wind_file_cache_entry_t** earlier, 282 | wind_file_cache_entry_t** later) 283 | { 284 | assert(cache && earlier && later); 285 | 286 | *earlier = *later = NULL; 287 | 288 | // This is the best we can do if we have no entries. 289 | if(cache->n_entries == 0) 290 | return; 291 | 292 | // Search for earlier and later entries which match 293 | unsigned int i; 294 | for(i=0; in_entries; ++i) 295 | { 296 | wind_file_cache_entry_t* entry = cache->entries[i]; 297 | 298 | if(entry->timestamp <= timestamp) { 299 | // This is an earlier entry 300 | if(!(*earlier) || (entry->timestamp > (*earlier)->timestamp)) 301 | { 302 | if(wind_file_cache_entry_contains_point(entry,lat,lon)) 303 | *earlier = entry; 304 | } 305 | } else { 306 | // This is a later entry 307 | if(!(*later) || (entry->timestamp < (*later)->timestamp)) { 308 | if(wind_file_cache_entry_contains_point(entry,lat,lon)) 309 | *later = entry; 310 | } 311 | } 312 | } 313 | } 314 | 315 | const char* 316 | wind_file_cache_entry_file_path(wind_file_cache_entry_t* entry) 317 | { 318 | if(!entry) 319 | return NULL; 320 | return entry->filepath; 321 | } 322 | 323 | unsigned int 324 | wind_file_cache_entry_timestamp(wind_file_cache_entry_t* entry) 325 | { 326 | if(!entry) 327 | return 0; 328 | return entry->timestamp; 329 | } 330 | 331 | wind_file_t* 332 | wind_file_cache_entry_file(wind_file_cache_entry_t *entry) 333 | { 334 | const char* filepath; 335 | 336 | if(!entry) 337 | return NULL; 338 | 339 | if(entry->loaded_file) 340 | return entry->loaded_file; 341 | 342 | filepath = wind_file_cache_entry_file_path(entry); 343 | if(!filepath) 344 | return NULL; 345 | 346 | entry->loaded_file = wind_file_new(filepath); 347 | return entry->loaded_file; 348 | } 349 | 350 | // Data for God's own editor. 351 | // vim:sw=8:ts=8:et:cindent 352 | -------------------------------------------------------------------------------- /pred_src/ini/iniparser.h: -------------------------------------------------------------------------------- 1 | 2 | /*-------------------------------------------------------------------------*/ 3 | /** 4 | @file iniparser.h 5 | @author N. Devillard 6 | @date Sep 2007 7 | @version 3.0 8 | @brief Parser for ini files. 9 | */ 10 | /*--------------------------------------------------------------------------*/ 11 | 12 | /* 13 | $Id: iniparser.h,v 1.24 2007-11-23 21:38:19 ndevilla Exp $ 14 | $Revision: 1.24 $ 15 | */ 16 | 17 | #ifndef _INIPARSER_H_ 18 | #define _INIPARSER_H_ 19 | 20 | /*--------------------------------------------------------------------------- 21 | Includes 22 | ---------------------------------------------------------------------------*/ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | /* 29 | * The following #include is necessary on many Unixes but not Linux. 30 | * It is not needed for Windows platforms. 31 | * Uncomment it if needed. 32 | */ 33 | /* #include */ 34 | 35 | #include "dictionary.h" 36 | 37 | /*--------------------------------------------------------------------------- 38 | Macros 39 | ---------------------------------------------------------------------------*/ 40 | /** For backwards compatibility only */ 41 | #define iniparser_getstr(d, k) iniparser_getstring(d, k, NULL) 42 | #define iniparser_setstr iniparser_setstring 43 | 44 | /*-------------------------------------------------------------------------*/ 45 | /** 46 | @brief Get number of sections in a dictionary 47 | @param d Dictionary to examine 48 | @return int Number of sections found in dictionary 49 | 50 | This function returns the number of sections found in a dictionary. 51 | The test to recognize sections is done on the string stored in the 52 | dictionary: a section name is given as "section" whereas a key is 53 | stored as "section:key", thus the test looks for entries that do not 54 | contain a colon. 55 | 56 | This clearly fails in the case a section name contains a colon, but 57 | this should simply be avoided. 58 | 59 | This function returns -1 in case of error. 60 | */ 61 | /*--------------------------------------------------------------------------*/ 62 | 63 | int iniparser_getnsec(dictionary * d); 64 | 65 | 66 | /*-------------------------------------------------------------------------*/ 67 | /** 68 | @brief Get name for section n in a dictionary. 69 | @param d Dictionary to examine 70 | @param n Section number (from 0 to nsec-1). 71 | @return Pointer to char string 72 | 73 | This function locates the n-th section in a dictionary and returns 74 | its name as a pointer to a string statically allocated inside the 75 | dictionary. Do not free or modify the returned string! 76 | 77 | This function returns NULL in case of error. 78 | */ 79 | /*--------------------------------------------------------------------------*/ 80 | 81 | char * iniparser_getsecname(dictionary * d, int n); 82 | 83 | 84 | /*-------------------------------------------------------------------------*/ 85 | /** 86 | @brief Save a dictionary to a loadable ini file 87 | @param d Dictionary to dump 88 | @param f Opened file pointer to dump to 89 | @return void 90 | 91 | This function dumps a given dictionary into a loadable ini file. 92 | It is Ok to specify @c stderr or @c stdout as output files. 93 | */ 94 | /*--------------------------------------------------------------------------*/ 95 | 96 | void iniparser_dump_ini(dictionary * d, FILE * f); 97 | 98 | /*-------------------------------------------------------------------------*/ 99 | /** 100 | @brief Dump a dictionary to an opened file pointer. 101 | @param d Dictionary to dump. 102 | @param f Opened file pointer to dump to. 103 | @return void 104 | 105 | This function prints out the contents of a dictionary, one element by 106 | line, onto the provided file pointer. It is OK to specify @c stderr 107 | or @c stdout as output files. This function is meant for debugging 108 | purposes mostly. 109 | */ 110 | /*--------------------------------------------------------------------------*/ 111 | void iniparser_dump(dictionary * d, FILE * f); 112 | 113 | /*-------------------------------------------------------------------------*/ 114 | /** 115 | @brief Get the string associated to a key 116 | @param d Dictionary to search 117 | @param key Key string to look for 118 | @param def Default value to return if key not found. 119 | @return pointer to statically allocated character string 120 | 121 | This function queries a dictionary for a key. A key as read from an 122 | ini file is given as "section:key". If the key cannot be found, 123 | the pointer passed as 'def' is returned. 124 | The returned char pointer is pointing to a string allocated in 125 | the dictionary, do not free or modify it. 126 | */ 127 | /*--------------------------------------------------------------------------*/ 128 | char * iniparser_getstring(dictionary * d, const char * key, char * def); 129 | 130 | /*-------------------------------------------------------------------------*/ 131 | /** 132 | @brief Get the string associated to a key, convert to an int 133 | @param d Dictionary to search 134 | @param key Key string to look for 135 | @param notfound Value to return in case of error 136 | @return integer 137 | 138 | This function queries a dictionary for a key. A key as read from an 139 | ini file is given as "section:key". If the key cannot be found, 140 | the notfound value is returned. 141 | 142 | Supported values for integers include the usual C notation 143 | so decimal, octal (starting with 0) and hexadecimal (starting with 0x) 144 | are supported. Examples: 145 | 146 | - "42" -> 42 147 | - "042" -> 34 (octal -> decimal) 148 | - "0x42" -> 66 (hexa -> decimal) 149 | 150 | Warning: the conversion may overflow in various ways. Conversion is 151 | totally outsourced to strtol(), see the associated man page for overflow 152 | handling. 153 | 154 | Credits: Thanks to A. Becker for suggesting strtol() 155 | */ 156 | /*--------------------------------------------------------------------------*/ 157 | int iniparser_getint(dictionary * d, const char * key, int notfound); 158 | 159 | /*-------------------------------------------------------------------------*/ 160 | /** 161 | @brief Get the string associated to a key, convert to a double 162 | @param d Dictionary to search 163 | @param key Key string to look for 164 | @param notfound Value to return in case of error 165 | @return double 166 | 167 | This function queries a dictionary for a key. A key as read from an 168 | ini file is given as "section:key". If the key cannot be found, 169 | the notfound value is returned. 170 | */ 171 | /*--------------------------------------------------------------------------*/ 172 | double iniparser_getdouble(dictionary * d, char * key, double notfound); 173 | 174 | /*-------------------------------------------------------------------------*/ 175 | /** 176 | @brief Get the string associated to a key, convert to a boolean 177 | @param d Dictionary to search 178 | @param key Key string to look for 179 | @param notfound Value to return in case of error 180 | @return integer 181 | 182 | This function queries a dictionary for a key. A key as read from an 183 | ini file is given as "section:key". If the key cannot be found, 184 | the notfound value is returned. 185 | 186 | A true boolean is found if one of the following is matched: 187 | 188 | - A string starting with 'y' 189 | - A string starting with 'Y' 190 | - A string starting with 't' 191 | - A string starting with 'T' 192 | - A string starting with '1' 193 | 194 | A false boolean is found if one of the following is matched: 195 | 196 | - A string starting with 'n' 197 | - A string starting with 'N' 198 | - A string starting with 'f' 199 | - A string starting with 'F' 200 | - A string starting with '0' 201 | 202 | The notfound value returned if no boolean is identified, does not 203 | necessarily have to be 0 or 1. 204 | */ 205 | /*--------------------------------------------------------------------------*/ 206 | int iniparser_getboolean(dictionary * d, const char * key, int notfound); 207 | 208 | 209 | /*-------------------------------------------------------------------------*/ 210 | /** 211 | @brief Set an entry in a dictionary. 212 | @param ini Dictionary to modify. 213 | @param entry Entry to modify (entry name) 214 | @param val New value to associate to the entry. 215 | @return int 0 if Ok, -1 otherwise. 216 | 217 | If the given entry can be found in the dictionary, it is modified to 218 | contain the provided value. If it cannot be found, -1 is returned. 219 | It is Ok to set val to NULL. 220 | */ 221 | /*--------------------------------------------------------------------------*/ 222 | int iniparser_setstring(dictionary * ini, char * entry, char * val); 223 | 224 | 225 | /*-------------------------------------------------------------------------*/ 226 | /** 227 | @brief Delete an entry in a dictionary 228 | @param ini Dictionary to modify 229 | @param entry Entry to delete (entry name) 230 | @return void 231 | 232 | If the given entry can be found, it is deleted from the dictionary. 233 | */ 234 | /*--------------------------------------------------------------------------*/ 235 | void iniparser_unset(dictionary * ini, char * entry); 236 | 237 | /*-------------------------------------------------------------------------*/ 238 | /** 239 | @brief Finds out if a given entry exists in a dictionary 240 | @param ini Dictionary to search 241 | @param entry Name of the entry to look for 242 | @return integer 1 if entry exists, 0 otherwise 243 | 244 | Finds out if a given entry exists in the dictionary. Since sections 245 | are stored as keys with NULL associated values, this is the only way 246 | of querying for the presence of sections in a dictionary. 247 | */ 248 | /*--------------------------------------------------------------------------*/ 249 | int iniparser_find_entry(dictionary * ini, char * entry) ; 250 | 251 | /*-------------------------------------------------------------------------*/ 252 | /** 253 | @brief Parse an ini file and return an allocated dictionary object 254 | @param ininame Name of the ini file to read. 255 | @return Pointer to newly allocated dictionary 256 | 257 | This is the parser for ini files. This function is called, providing 258 | the name of the file to be read. It returns a dictionary object that 259 | should not be accessed directly, but through accessor functions 260 | instead. 261 | 262 | The returned dictionary must be freed using iniparser_freedict(). 263 | */ 264 | /*--------------------------------------------------------------------------*/ 265 | dictionary * iniparser_load(const char * ininame); 266 | 267 | /*-------------------------------------------------------------------------*/ 268 | /** 269 | @brief Parse an ini file and return an allocated dictionary object 270 | @param file FILE* associated with file to read 271 | @return Pointer to newly allocated dictionary 272 | 273 | This is the parser for ini files. This function is called, providing 274 | the name of the file to be read. It returns a dictionary object that 275 | should not be accessed directly, but through accessor functions 276 | instead. 277 | 278 | The returned dictionary must be freed using iniparser_freedict(). 279 | */ 280 | /*--------------------------------------------------------------------------*/ 281 | dictionary * iniparser_loadfile(FILE * file); 282 | 283 | /*-------------------------------------------------------------------------*/ 284 | /** 285 | @brief Free all memory associated to an ini dictionary 286 | @param d Dictionary to free 287 | @return void 288 | 289 | Free all memory associated to an ini dictionary. 290 | It is mandatory to call this function before the dictionary object 291 | gets out of the current context. 292 | */ 293 | /*--------------------------------------------------------------------------*/ 294 | void iniparser_freedict(dictionary * d); 295 | 296 | #endif 297 | -------------------------------------------------------------------------------- /predict/js/calc/calc.js: -------------------------------------------------------------------------------- 1 | function get_value(id) { 2 | return parseFloat(document.getElementById(id).value); 3 | } 4 | 5 | function clear_errors() { 6 | var ids = ['mp', 'tar', 'tba', 'rho_g', 'rho_a', 'adm', 'bd', 'cd', 7 | 'bd_c', 'cd_c']; 8 | 9 | for(var i in ids) { 10 | document.getElementById(ids[i]).style.backgroundColor = ''; 11 | } 12 | 13 | var ids = ['mp_w', 'mb_w', 'tar_w', 'tba_w']; 14 | for(i in ids) { 15 | document.getElementById(ids[i]).innerHTML = ' '; 16 | } 17 | } 18 | 19 | function show_error(id) { 20 | document.getElementById(id).style.backgroundColor = '#f99'; 21 | } 22 | 23 | function set_error(id, error) { 24 | show_error(id); 25 | document.getElementById(id+"_w").innerHTML = error; 26 | } 27 | 28 | function sanity_check_inputs(mb, mp, mp_set, tar, tba, tar_set, tba_set) { 29 | if(tar_set && tba_set) { 30 | set_error('tar', "Can't specify both!"); 31 | set_error('tba', "Can't specify both!"); 32 | return 1; 33 | } else if(!tar_set && !tba_set) { 34 | set_error('tar', "Must specify at least one!"); 35 | set_error('tba', "Must specify at least one!"); 36 | return 1; 37 | } 38 | 39 | if(tar_set && tar < 0) { 40 | set_error('tar', "Can't be negative!"); 41 | return 1; 42 | } else if(tar_set && tar > 10) { 43 | set_error('tar', "Too large! (> 10m/s)"); 44 | return 1; 45 | } 46 | 47 | if(tba_set && tba < 10000) { 48 | set_error('tba', "Too low! (< 10km)"); 49 | return 1; 50 | } else if(tba_set && tba > 40000) { 51 | set_error('tba', "Too high! (> 40km)"); 52 | return 1; 53 | } 54 | 55 | if(!mp_set) { 56 | set_error('mp', "Mass required!"); 57 | return 1; 58 | } else if(mp < 20) { 59 | set_error('mp', "Too small! (< 20g)"); 60 | return 1; 61 | } else if(mp > 5000) { 62 | set_error('mp', "Too large! (> 5kg)"); 63 | return 1; 64 | } 65 | 66 | return 0; 67 | 68 | } 69 | 70 | function sanity_check_constants(rho_g, rho_a, adm, ga, bd, cd) { 71 | if(!rho_a || rho_a < 0) { 72 | show_error('rho_a'); 73 | return 1; 74 | } 75 | if(!rho_g || rho_g < 0 || rho_g > rho_a) { 76 | show_error('rho_g'); 77 | return 1; 78 | } 79 | if(!adm || adm < 0) { 80 | show_error('adm'); 81 | return 1; 82 | } 83 | if(!ga || ga < 0) { 84 | show_error('ga'); 85 | return 1; 86 | } 87 | if(!cd || cd < 0 || cd > 1) { 88 | show_error('cd'); 89 | return 1; 90 | } 91 | if(!bd || bd < 0) { 92 | show_error('bd'); 93 | return 1; 94 | } 95 | 96 | return 0; 97 | } 98 | 99 | function find_rho_g() { 100 | var gas = document.getElementById('gas').value; 101 | var rho_g; 102 | 103 | switch(gas) { 104 | case 'he': 105 | rho_g = 0.1786; 106 | document.getElementById('rho_g').value = rho_g; 107 | document.getElementById('rho_g').disabled = "disabled"; 108 | break; 109 | case 'h': 110 | rho_g = 0.0899; 111 | document.getElementById('rho_g').value = rho_g; 112 | document.getElementById('rho_g').disabled = "disabled"; 113 | break; 114 | case 'ch4': 115 | rho_g = 0.6672; 116 | document.getElementById('rho_g').value = rho_g; 117 | document.getElementById('rho_g').disabled = "disabled"; 118 | break; 119 | default: 120 | document.getElementById('rho_g').disabled = ""; 121 | rho_g = get_value('rho_g'); 122 | break; 123 | } 124 | 125 | return rho_g; 126 | } 127 | 128 | function find_bd(mb) { 129 | var bds = new Array(); 130 | 131 | // From Kaymont Totex Sounding Balloon Data 132 | bds["k200"] = 3.00; 133 | bds["k300"] = 3.78; 134 | bds["k350"] = 4.12; 135 | bds["k450"] = 4.72; 136 | bds["k500"] = 4.99; 137 | bds["k600"] = 6.02; 138 | bds["k700"] = 6.53; 139 | bds["k800"] = 7.00; 140 | bds["k1000"] = 7.86; 141 | bds["k1200"] = 8.63; 142 | bds["k1500"] = 9.44; 143 | bds["k2000"] = 10.54; 144 | bds["k3000"] = 13.00; 145 | // Hwoyee data from http://www.hwoyee.com/base.asp?ScClassid=521&id=521102 146 | bds["h200"] = 3.00; 147 | bds["h300"] = 3.80; 148 | bds["h350"] = 4.10; 149 | bds["h400"] = 4.50; 150 | bds["h500"] = 5.00; 151 | bds["h600"] = 5.80; 152 | bds["h750"] = 6.50; 153 | bds["h800"] = 6.80; 154 | bds["h950"] = 7.20; 155 | bds["h1000"] = 7.50; 156 | bds["h1200"] = 8.50; 157 | bds["h1500"] = 9.50; 158 | bds["h1600"] = 10.50; 159 | bds["h2000"] = 11.00; 160 | // PAWAN data from 161 | // https://sites.google.com/site/balloonnewswebstore/1200g-balloon-data 162 | bds["p1200"] = 8.0; 163 | 164 | 165 | var bd_c = document.getElementById('bd_c').checked; 166 | var bd; 167 | 168 | if(bd_c) { 169 | bd = get_value('bd'); 170 | document.getElementById('bd').disabled = ""; 171 | } else { 172 | bd = bds[mb]; 173 | document.getElementById('bd').disabled = "disabled"; 174 | document.getElementById('bd').value = bd; 175 | } 176 | 177 | return bd; 178 | } 179 | 180 | function find_cd(mb) { 181 | var cds = new Array(); 182 | 183 | // From Kaymont Totex Sounding Balloon Data 184 | cds["k200"] = 0.25; 185 | cds["k300"] = 0.25; 186 | cds["k350"] = 0.25; 187 | cds["k450"] = 0.25; 188 | cds["k500"] = 0.25; 189 | cds["k600"] = 0.30; 190 | cds["k700"] = 0.30; 191 | cds["k800"] = 0.30; 192 | cds["k1000"] = 0.30; 193 | cds["k1200"] = 0.25; 194 | cds["k1500"] = 0.25; 195 | cds["k2000"] = 0.25; 196 | cds["k3000"] = 0.25; 197 | // Hwoyee data just guesswork 198 | cds["h200"] = 0.25; 199 | cds["h300"] = 0.25; 200 | cds["h350"] = 0.25; 201 | cds["h400"] = 0.25; 202 | cds["h500"] = 0.25; 203 | cds["h600"] = 0.30; 204 | cds["h750"] = 0.30; 205 | cds["h800"] = 0.30; 206 | cds["h950"] = 0.30; 207 | cds["h1000"] = 0.30; 208 | cds["h1200"] = 0.25; 209 | cds["h1500"] = 0.25; 210 | cds["h1600"] = 0.25; 211 | cds["h2000"] = 0.25; 212 | // PAWAN data also guesswork 213 | cds["p1200"] = 0.25; 214 | 215 | var cd_c = document.getElementById('cd_c').checked; 216 | var cd; 217 | 218 | if(cd_c) { 219 | cd = get_value('cd'); 220 | document.getElementById('cd').disabled = ""; 221 | } else { 222 | cd = cds[mb]; 223 | document.getElementById('cd').disabled = "disabled"; 224 | document.getElementById('cd').value = cd; 225 | } 226 | 227 | return cd; 228 | } 229 | 230 | function calc_update() { 231 | // Reset error status 232 | clear_errors(); 233 | 234 | // Get input values and check them 235 | var mb = document.getElementById('mb').value; 236 | var mp = get_value('mp'); 237 | var tar = get_value('tar'); 238 | var tba = get_value('tba'); 239 | var mp_set = 0; 240 | var tar_set = 0; 241 | var tba_set = 0; 242 | 243 | if(document.getElementById('mp').value) 244 | mp_set = 1; 245 | if(document.getElementById('tar').value) 246 | tar_set = 1; 247 | if(document.getElementById('tba').value) 248 | tba_set = 1; 249 | 250 | if(sanity_check_inputs(mb, mp, mp_set, tar, tba, tar_set, tba_set)) 251 | return; 252 | 253 | // Get constants and check them 254 | var rho_g = find_rho_g(); 255 | var rho_a = get_value('rho_a'); 256 | var adm = get_value('adm'); 257 | var ga = get_value('ga'); 258 | var bd = find_bd(mb); 259 | var cd = find_cd(mb); 260 | 261 | if(sanity_check_constants(rho_g, rho_a, adm, ga, bd, cd)) 262 | return; 263 | 264 | // Do some maths 265 | mb = parseFloat(mb.substr(1)) / 1000.0; 266 | mp = mp / 1000.0; 267 | 268 | var ascent_rate = 0; 269 | var burst_altitude = 0; 270 | var time_to_burst = 0; 271 | var neck_lift = 0; 272 | var launch_radius = 0; 273 | var launch_volume = 0; 274 | 275 | var burst_volume = (4.0/3.0) * Math.PI * Math.pow(bd / 2.0, 3); 276 | 277 | if(tba_set) { 278 | launch_volume = burst_volume * Math.exp((-tba) / adm); 279 | launch_radius = Math.pow((3*launch_volume)/(4*Math.PI), (1/3)); 280 | } else if(tar_set) { 281 | var a = ga * (rho_a - rho_g) * (4.0 / 3.0) * Math.PI; 282 | var b = -0.5 * Math.pow(tar, 2) * cd * rho_a * Math.PI; 283 | var c = 0; 284 | var d = - (mp + mb) * ga; 285 | 286 | var f = (((3*c)/a) - (Math.pow(b, 2) / Math.pow(a,2)) / 3.0); 287 | var g = ( 288 | ((2*Math.pow(b,3))/Math.pow(a,3)) - 289 | ((9*b*c)/(Math.pow(a,2))) + ((27*d)/a) / 27.0 290 | ); 291 | var h = (Math.pow(g,2) / 4.0) + (Math.pow(f,3) / 27.0); 292 | 293 | if(h>0) { 294 | // One real root. This is what should happen. 295 | var R = (-0.5 * g) + Math.sqrt(h); 296 | var S = Math.pow(R, 1.0/3.0); 297 | var T = (-0.5 * g) - Math.sqrt(h); 298 | var U = Math.pow(T, 1.0/3.0); 299 | launch_radius = (S+U) - (b/(3*a)); 300 | } else if(f==0 && g==0 && h==0) { 301 | // Three real and equal roots 302 | // Will this ever even happen? 303 | launch_radius = -1 * Math.pow(d/a, 1.0/3.0); 304 | } else if(h <= 0) { 305 | // Three real and different roots 306 | // What the hell do we do?! 307 | // It needs trig! fffff 308 | var i = Math.sqrt((Math.pow(g,2)/4.0) - h); 309 | var j = Math.pow(i, 1.0/3.0); 310 | var k = Math.acos(-g / (2*i)); 311 | var L = -1 * j; 312 | var M = Math.cos(K/3.0); 313 | var N = Math.sqrt(3) * Math.sin(K/3.0); 314 | var P = (b/(3*a)) * -1; 315 | var r1 = 2*j*Math.cos(k/3.0) - (b/(3*a)); 316 | var r2 = L * (M + N) + P; 317 | var r3 = L * (M - N) + P; 318 | 319 | alert("Three possible solutions found: " 320 | + r1 + ", " + r2 + ", " + r3); 321 | 322 | if(r1 > 0) { 323 | launch_radius = r1; 324 | } else if(r2 > 0) { 325 | launch_radius = r2; 326 | } else if(r3 > 0) { 327 | launch_radius = r3; 328 | } 329 | } else { 330 | // No real roots 331 | } 332 | } 333 | 334 | var launch_area = Math.PI * Math.pow(launch_radius, 2); 335 | var launch_volume = (4.0/3.0) * Math.PI * Math.pow(launch_radius, 3); 336 | var density_difference = rho_a - rho_g; 337 | var gross_lift = launch_volume * density_difference; 338 | neck_lift = (gross_lift - mb) * 1000; 339 | var total_mass = mp + mb; 340 | var free_lift = (gross_lift - total_mass) * ga; 341 | ascent_rate = Math.sqrt(free_lift / (0.5 * cd * launch_area * rho_a)); 342 | var volume_ratio = launch_volume / burst_volume; 343 | burst_altitude = -(adm) * Math.log(volume_ratio); 344 | time_to_burst = (burst_altitude / ascent_rate) / 60.0; 345 | 346 | if(isNaN(ascent_rate)) { 347 | set_error('tba', "Altitude unreachable
for this configuration."); 348 | return; 349 | } 350 | 351 | ascent_rate = ascent_rate.toFixed(2); 352 | burst_altitude = burst_altitude.toFixed(); 353 | time_to_burst = time_to_burst.toFixed(); 354 | neck_lift = neck_lift.toFixed(); 355 | launch_litres = (launch_volume * 1000).toFixed(); 356 | launch_cf = (launch_volume * 35.31).toFixed(1); 357 | launch_volume = launch_volume.toFixed(2); 358 | 359 | document.getElementById('ar').innerHTML = ascent_rate; 360 | document.getElementById('ba').innerHTML = burst_altitude; 361 | document.getElementById('ttb').innerHTML = time_to_burst + " min"; 362 | document.getElementById('nl').innerHTML = neck_lift + " g"; 363 | document.getElementById('lv_m3').innerHTML = launch_volume + " m3"; 364 | document.getElementById('lv_l').innerHTML = launch_litres + " L"; 365 | document.getElementById('lv_cf').innerHTML = launch_cf + " ft3"; 366 | } 367 | 368 | function calc_init() { 369 | 370 | var ids = ['mb', 'mp', 'tar', 'tba', 'gas', 'rho_g', 'rho_a', 'adm', 'bd', 'cd', 'bd_c', 'cd_c']; 371 | for(var i in ids) { 372 | document.getElementById(ids[i]).onchange = calc_update; 373 | } 374 | calc_update(); 375 | } 376 | -------------------------------------------------------------------------------- /pred_src/ini/dictionary.c: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------*/ 2 | /** 3 | @file dictionary.c 4 | @author N. Devillard 5 | @date Sep 2007 6 | @version $Revision: 1.27 $ 7 | @brief Implements a dictionary for string variables. 8 | 9 | This module implements a simple dictionary object, i.e. a list 10 | of string/string associations. This object is useful to store e.g. 11 | informations retrieved from a configuration file (ini files). 12 | */ 13 | /*--------------------------------------------------------------------------*/ 14 | 15 | /* 16 | $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $ 17 | $Revision: 1.27 $ 18 | */ 19 | /*--------------------------------------------------------------------------- 20 | Includes 21 | ---------------------------------------------------------------------------*/ 22 | #include "dictionary.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /** Maximum value size for integers and doubles. */ 30 | #define MAXVALSZ 1024 31 | 32 | /** Minimal allocated number of entries in a dictionary */ 33 | #define DICTMINSZ 128 34 | 35 | /** Invalid key token */ 36 | #define DICT_INVALID_KEY ((char*)-1) 37 | 38 | /*--------------------------------------------------------------------------- 39 | Private functions 40 | ---------------------------------------------------------------------------*/ 41 | 42 | /* Doubles the allocated size associated to a pointer */ 43 | /* 'size' is the current allocated size. */ 44 | static void * mem_double(void * ptr, int size) 45 | { 46 | void * newptr ; 47 | 48 | newptr = calloc(2*size, 1); 49 | if (newptr==NULL) { 50 | return NULL ; 51 | } 52 | memcpy(newptr, ptr, size); 53 | free(ptr); 54 | return newptr ; 55 | } 56 | 57 | /*-------------------------------------------------------------------------*/ 58 | /** 59 | @brief Duplicate a string 60 | @param s String to duplicate 61 | @return Pointer to a newly allocated string, to be freed with free() 62 | 63 | This is a replacement for strdup(). This implementation is provided 64 | for systems that do not have it. 65 | */ 66 | /*--------------------------------------------------------------------------*/ 67 | static char * xstrdup(char * s) 68 | { 69 | char * t ; 70 | if (!s) 71 | return NULL ; 72 | t = malloc(strlen(s)+1) ; 73 | if (t) { 74 | strcpy(t,s); 75 | } 76 | return t ; 77 | } 78 | 79 | /*--------------------------------------------------------------------------- 80 | Function codes 81 | ---------------------------------------------------------------------------*/ 82 | /*-------------------------------------------------------------------------*/ 83 | /** 84 | @brief Compute the hash key for a string. 85 | @param key Character string to use for key. 86 | @return 1 unsigned int on at least 32 bits. 87 | 88 | This hash function has been taken from an Article in Dr Dobbs Journal. 89 | This is normally a collision-free function, distributing keys evenly. 90 | The key is stored anyway in the struct so that collision can be avoided 91 | by comparing the key itself in last resort. 92 | */ 93 | /*--------------------------------------------------------------------------*/ 94 | unsigned dictionary_hash(char * key) 95 | { 96 | int len ; 97 | unsigned hash ; 98 | int i ; 99 | 100 | len = strlen(key); 101 | for (hash=0, i=0 ; i>6) ; 105 | } 106 | hash += (hash <<3); 107 | hash ^= (hash >>11); 108 | hash += (hash <<15); 109 | return hash ; 110 | } 111 | 112 | /*-------------------------------------------------------------------------*/ 113 | /** 114 | @brief Create a new dictionary object. 115 | @param size Optional initial size of the dictionary. 116 | @return 1 newly allocated dictionary objet. 117 | 118 | This function allocates a new dictionary object of given size and returns 119 | it. If you do not know in advance (roughly) the number of entries in the 120 | dictionary, give size=0. 121 | */ 122 | /*--------------------------------------------------------------------------*/ 123 | dictionary * dictionary_new(int size) 124 | { 125 | dictionary * d ; 126 | 127 | /* If no size was specified, allocate space for DICTMINSZ */ 128 | if (sizesize = size ; 134 | d->val = (char **)calloc(size, sizeof(char*)); 135 | d->key = (char **)calloc(size, sizeof(char*)); 136 | d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); 137 | return d ; 138 | } 139 | 140 | /*-------------------------------------------------------------------------*/ 141 | /** 142 | @brief Delete a dictionary object 143 | @param d dictionary object to deallocate. 144 | @return void 145 | 146 | Deallocate a dictionary object and all memory associated to it. 147 | */ 148 | /*--------------------------------------------------------------------------*/ 149 | void dictionary_del(dictionary * d) 150 | { 151 | int i ; 152 | 153 | if (d==NULL) return ; 154 | for (i=0 ; isize ; i++) { 155 | if (d->key[i]!=NULL) 156 | free(d->key[i]); 157 | if (d->val[i]!=NULL) 158 | free(d->val[i]); 159 | } 160 | free(d->val); 161 | free(d->key); 162 | free(d->hash); 163 | free(d); 164 | return ; 165 | } 166 | 167 | /*-------------------------------------------------------------------------*/ 168 | /** 169 | @brief Get a value from a dictionary. 170 | @param d dictionary object to search. 171 | @param key Key to look for in the dictionary. 172 | @param def Default value to return if key not found. 173 | @return 1 pointer to internally allocated character string. 174 | 175 | This function locates a key in a dictionary and returns a pointer to its 176 | value, or the passed 'def' pointer if no such key can be found in 177 | dictionary. The returned character pointer points to data internal to the 178 | dictionary object, you should not try to free it or modify it. 179 | */ 180 | /*--------------------------------------------------------------------------*/ 181 | char * dictionary_get(dictionary * d, char * key, char * def) 182 | { 183 | unsigned hash ; 184 | int i ; 185 | 186 | hash = dictionary_hash(key); 187 | for (i=0 ; isize ; i++) { 188 | if (d->key[i]==NULL) 189 | continue ; 190 | /* Compare hash */ 191 | if (hash==d->hash[i]) { 192 | /* Compare string, to avoid hash collisions */ 193 | if (!strcmp(key, d->key[i])) { 194 | return d->val[i] ; 195 | } 196 | } 197 | } 198 | return def ; 199 | } 200 | 201 | /*-------------------------------------------------------------------------*/ 202 | /** 203 | @brief Set a value in a dictionary. 204 | @param d dictionary object to modify. 205 | @param key Key to modify or add. 206 | @param val Value to add. 207 | @return int 0 if Ok, anything else otherwise 208 | 209 | If the given key is found in the dictionary, the associated value is 210 | replaced by the provided one. If the key cannot be found in the 211 | dictionary, it is added to it. 212 | 213 | It is Ok to provide a NULL value for val, but NULL values for the dictionary 214 | or the key are considered as errors: the function will return immediately 215 | in such a case. 216 | 217 | Notice that if you dictionary_set a variable to NULL, a call to 218 | dictionary_get will return a NULL value: the variable will be found, and 219 | its value (NULL) is returned. In other words, setting the variable 220 | content to NULL is equivalent to deleting the variable from the 221 | dictionary. It is not possible (in this implementation) to have a key in 222 | the dictionary without value. 223 | 224 | This function returns non-zero in case of failure. 225 | */ 226 | /*--------------------------------------------------------------------------*/ 227 | int dictionary_set(dictionary * d, char * key, char * val) 228 | { 229 | int i ; 230 | unsigned hash ; 231 | 232 | if (d==NULL || key==NULL) return -1 ; 233 | 234 | /* Compute hash for this key */ 235 | hash = dictionary_hash(key) ; 236 | /* Find if value is already in dictionary */ 237 | if (d->n>0) { 238 | for (i=0 ; isize ; i++) { 239 | if (d->key[i]==NULL) 240 | continue ; 241 | if (hash==d->hash[i]) { /* Same hash value */ 242 | if (!strcmp(key, d->key[i])) { /* Same key */ 243 | /* Found a value: modify and return */ 244 | if (d->val[i]!=NULL) 245 | free(d->val[i]); 246 | d->val[i] = val ? xstrdup(val) : NULL ; 247 | /* Value has been modified: return */ 248 | return 0 ; 249 | } 250 | } 251 | } 252 | } 253 | /* Add a new value */ 254 | /* See if dictionary needs to grow */ 255 | if (d->n==d->size) { 256 | 257 | /* Reached maximum size: reallocate dictionary */ 258 | d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ; 259 | d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ; 260 | d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; 261 | if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { 262 | /* Cannot grow dictionary */ 263 | return -1 ; 264 | } 265 | /* Double size */ 266 | d->size *= 2 ; 267 | } 268 | 269 | /* Insert key in the first empty slot */ 270 | for (i=0 ; isize ; i++) { 271 | if (d->key[i]==NULL) { 272 | /* Add key here */ 273 | break ; 274 | } 275 | } 276 | /* Copy key */ 277 | d->key[i] = xstrdup(key); 278 | d->val[i] = val ? xstrdup(val) : NULL ; 279 | d->hash[i] = hash; 280 | d->n ++ ; 281 | return 0 ; 282 | } 283 | 284 | /*-------------------------------------------------------------------------*/ 285 | /** 286 | @brief Delete a key in a dictionary 287 | @param d dictionary object to modify. 288 | @param key Key to remove. 289 | @return void 290 | 291 | This function deletes a key in a dictionary. Nothing is done if the 292 | key cannot be found. 293 | */ 294 | /*--------------------------------------------------------------------------*/ 295 | void dictionary_unset(dictionary * d, char * key) 296 | { 297 | unsigned hash ; 298 | int i ; 299 | 300 | if (key == NULL) { 301 | return; 302 | } 303 | 304 | hash = dictionary_hash(key); 305 | for (i=0 ; isize ; i++) { 306 | if (d->key[i]==NULL) 307 | continue ; 308 | /* Compare hash */ 309 | if (hash==d->hash[i]) { 310 | /* Compare string, to avoid hash collisions */ 311 | if (!strcmp(key, d->key[i])) { 312 | /* Found key */ 313 | break ; 314 | } 315 | } 316 | } 317 | if (i>=d->size) 318 | /* Key not found */ 319 | return ; 320 | 321 | free(d->key[i]); 322 | d->key[i] = NULL ; 323 | if (d->val[i]!=NULL) { 324 | free(d->val[i]); 325 | d->val[i] = NULL ; 326 | } 327 | d->hash[i] = 0 ; 328 | d->n -- ; 329 | return ; 330 | } 331 | 332 | /*-------------------------------------------------------------------------*/ 333 | /** 334 | @brief Dump a dictionary to an opened file pointer. 335 | @param d Dictionary to dump 336 | @param f Opened file pointer. 337 | @return void 338 | 339 | Dumps a dictionary onto an opened file pointer. Key pairs are printed out 340 | as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as 341 | output file pointers. 342 | */ 343 | /*--------------------------------------------------------------------------*/ 344 | void dictionary_dump(dictionary * d, FILE * out) 345 | { 346 | int i ; 347 | 348 | if (d==NULL || out==NULL) return ; 349 | if (d->n<1) { 350 | fprintf(out, "empty dictionary\n"); 351 | return ; 352 | } 353 | for (i=0 ; isize ; i++) { 354 | if (d->key[i]) { 355 | fprintf(out, "%20s\t[%s]\n", 356 | d->key[i], 357 | d->val[i] ? d->val[i] : "UNDEF"); 358 | } 359 | } 360 | return ; 361 | } 362 | 363 | 364 | /* Test code */ 365 | #ifdef TESTDIC 366 | #define NVALS 20000 367 | int main(int argc, char *argv[]) 368 | { 369 | dictionary * d ; 370 | char * val ; 371 | int i ; 372 | char cval[90] ; 373 | 374 | /* Allocate dictionary */ 375 | printf("allocating...\n"); 376 | d = dictionary_new(0); 377 | 378 | /* Set values in dictionary */ 379 | printf("setting %d values...\n", NVALS); 380 | for (i=0 ; in != 0) { 398 | printf("error deleting values\n"); 399 | } 400 | printf("deallocating...\n"); 401 | dictionary_del(d); 402 | return 0 ; 403 | } 404 | #endif 405 | /* vim: set ts=4 et sw=4 tw=75 */ 406 | -------------------------------------------------------------------------------- /pred_src/pred.c: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------- 2 | // CU Spaceflight Landing Prediction 3 | // Copyright (c) CU Spaceflight 2009, All Right Reserved 4 | // 5 | // Written by Rob Anderson 6 | // Modified by Fergus Noble 7 | // 8 | // THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 9 | // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 10 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 11 | // PARTICULAR PURPOSE. 12 | // -------------------------------------------------------------- 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "ini/iniparser.h" 22 | #include "util/gopt.h" 23 | #include "wind/wind_file_cache.h" 24 | 25 | #include "run_model.h" 26 | #include "pred.h" 27 | #include "altitude.h" 28 | 29 | FILE* output; 30 | FILE* kml_file; 31 | const char* data_dir; 32 | int verbosity; 33 | 34 | int main(int argc, const char *argv[]) { 35 | 36 | const char* argument; 37 | 38 | long int initial_timestamp; 39 | float initial_lat, initial_lng, initial_alt; 40 | float burst_alt, ascent_rate, drag_coeff, rmswinderror; 41 | int descent_mode; 42 | int scenario_idx, n_scenarios; 43 | int alarm_time; 44 | char* endptr; // used to check for errors on strtod calls 45 | 46 | wind_file_cache_t* file_cache; 47 | dictionary* scenario = NULL; 48 | 49 | // configure command-line options parsing 50 | void *options = gopt_sort(&argc, argv, gopt_start( 51 | gopt_option('h', 0, gopt_shorts('h', '?'), gopt_longs("help")), 52 | gopt_option('z', 0, gopt_shorts(0), gopt_longs("version")), 53 | gopt_option('v', GOPT_REPEAT, gopt_shorts('v'), gopt_longs("verbose")), 54 | gopt_option('o', GOPT_ARG, gopt_shorts('o'), gopt_longs("output")), 55 | gopt_option('k', GOPT_ARG, gopt_shorts('k'), gopt_longs("kml")), 56 | gopt_option('t', GOPT_ARG, gopt_shorts('t'), gopt_longs("start_time")), 57 | gopt_option('i', GOPT_ARG, gopt_shorts('i'), gopt_longs("data_dir")), 58 | gopt_option('d', 0, gopt_shorts('d'), gopt_longs("descending")), 59 | gopt_option('e', GOPT_ARG, gopt_shorts('e'), gopt_longs("wind_error")), 60 | gopt_option('a', GOPT_ARG, gopt_shorts('a'), gopt_longs("alarm")) 61 | )); 62 | 63 | if (gopt(options, 'h')) { 64 | // Help! 65 | // Print usage information 66 | printf("Usage: %s [options] [scenario files]\n", argv[0]); 67 | printf("Options:\n\n"); 68 | printf(" -h --help Display this information.\n"); 69 | printf(" --version Display version information.\n"); 70 | printf(" -v --verbose Display more information while running,\n"); 71 | printf(" Use -vv, -vvv etc. for even more verbose output.\n"); 72 | printf(" -t --start_time Start time of model, defaults to current time.\n"); 73 | printf(" Should be a UNIX standard format timestamp.\n"); 74 | printf(" -o --output Output file for CSV data, defaults to stdout. Overrides scenario.\n"); 75 | printf(" -k --kml Output KML file.\n"); 76 | printf(" -d --descending We are in the descent phase of the flight, i.e. after\n"); 77 | printf(" burst or cutdown. burst_alt and ascent_rate ignored.\n"); 78 | printf(" -i --data_dir Input directory for wind data, defaults to current dir.\n\n"); 79 | printf(" -e --wind_error RMS windspeed error (m/s).\n"); 80 | printf(" -a --alarm Use alarm() to kill pred incase it hangs.\n"); 81 | printf("The scenario file is an INI-like file giving the launch scenario. If it is\n"); 82 | printf("omitted, the scenario is read from standard input.\n"); 83 | exit(0); 84 | } 85 | 86 | if (gopt(options, 'z')) { 87 | // Version information 88 | printf("Landing Prediction version: %s\nCopyright (c) CU Spaceflight 2009\n", VERSION); 89 | exit(0); 90 | } 91 | 92 | if (gopt_arg(options, 'a', &argument) && strcmp(argument, "-")) { 93 | alarm_time = strtol(argument, &endptr, 0); 94 | if (endptr == argument) { 95 | fprintf(stderr, "ERROR: %s: invalid alarm length\n", argument); 96 | exit(1); 97 | } 98 | alarm(alarm_time); 99 | } 100 | 101 | verbosity = gopt(options, 'v'); 102 | 103 | if (gopt(options, 'd')) 104 | descent_mode = DESCENT_MODE_DESCENDING; 105 | else 106 | descent_mode = DESCENT_MODE_NORMAL; 107 | 108 | if (gopt_arg(options, 'k', &argument) && strcmp(argument, "-")) { 109 | kml_file = fopen(argument, "wb"); 110 | if (!kml_file) { 111 | fprintf(stderr, "ERROR: %s: could not open KML file for output\n", argument); 112 | exit(1); 113 | } 114 | } 115 | else 116 | kml_file = NULL; 117 | 118 | if (gopt_arg(options, 't', &argument) && strcmp(argument, "-")) { 119 | initial_timestamp = strtol(argument, &endptr, 0); 120 | if (endptr == argument) { 121 | fprintf(stderr, "ERROR: %s: invalid start timestamp\n", argument); 122 | exit(1); 123 | } 124 | } else { 125 | initial_timestamp = time(NULL); 126 | } 127 | 128 | if (!(gopt_arg(options, 'i', &data_dir) && strcmp(data_dir, "-"))) 129 | data_dir = "./"; 130 | 131 | 132 | // populate wind data file cache 133 | file_cache = wind_file_cache_new(data_dir); 134 | 135 | // read in flight parameters 136 | n_scenarios = argc - 1; 137 | if(n_scenarios == 0) { 138 | // we'll parse from std in 139 | n_scenarios = 1; 140 | } 141 | 142 | for(scenario_idx = 0; scenario_idx < n_scenarios; ++scenario_idx) { 143 | char* scenario_output = NULL; 144 | 145 | if(argc > scenario_idx+1) { 146 | scenario = iniparser_load(argv[scenario_idx+1]); 147 | } else { 148 | scenario = iniparser_loadfile(stdin); 149 | } 150 | 151 | if(!scenario) { 152 | fprintf(stderr, "ERROR: could not parse scanario file.\n"); 153 | exit(1); 154 | } 155 | 156 | if(verbosity > 1) { 157 | fprintf(stderr, "INFO: Parsed scenario file:\n"); 158 | iniparser_dump_ini(scenario, stderr); 159 | } 160 | 161 | scenario_output = iniparser_getstring(scenario, "output:filename", NULL); 162 | 163 | if (gopt_arg(options, 'o', &argument) && strcmp(argument, "-")) { 164 | if(verbosity > 0) { 165 | fprintf(stderr, "INFO: Writing output to file specified on command line: %s\n", argument); 166 | } 167 | output = fopen(argument, "wb"); 168 | if (!output) { 169 | fprintf(stderr, "ERROR: %s: could not open CSV file for output\n", argument); 170 | exit(1); 171 | } 172 | } else if (scenario_output != NULL) { 173 | if(verbosity > 0) { 174 | fprintf(stderr, "INFO: Writing output to file specified in scenario: %s\n", scenario_output); 175 | } 176 | output = fopen(scenario_output, "wb"); 177 | if (!output) { 178 | fprintf(stderr, "ERROR: %s: could not open CSV file for output\n", scenario_output); 179 | exit(1); 180 | } 181 | } else { 182 | if(verbosity > 0) { 183 | fprintf(stderr, "INFO: Writing output to stdout.\n"); 184 | } 185 | output = stdout; 186 | } 187 | 188 | // write KML header 189 | if (kml_file) 190 | start_kml(); 191 | 192 | // The observant amongst you will notice that there are default values for 193 | // *all* keys. This information should not be spread around too well. 194 | // Unfortunately, this means we lack some error checking. 195 | 196 | initial_lat = iniparser_getdouble(scenario, "launch-site:latitude", 0.0); 197 | initial_lng = iniparser_getdouble(scenario, "launch-site:longitude", 0.0); 198 | initial_alt = iniparser_getdouble(scenario, "launch-site:altitude", 0.0); 199 | 200 | ascent_rate = iniparser_getdouble(scenario, "altitude-model:ascent-rate", 1.0); 201 | 202 | // The 1.1045 comes from a magic constant buried in 203 | // ~cuspaceflight/public_html/predict/index.php. 204 | drag_coeff = iniparser_getdouble(scenario, "altitude-model:descent-rate", 1.0) * 1.1045; 205 | 206 | burst_alt = iniparser_getdouble(scenario, "altitude-model:burst-altitude", 1.0); 207 | 208 | rmswinderror = iniparser_getdouble(scenario, "atmosphere:wind-error", 0.0); 209 | if(gopt_arg(options, 'e', &argument) && strcmp(argument, "-")) { 210 | rmswinderror = strtod(argument, &endptr); 211 | if (endptr == argument) { 212 | fprintf(stderr, "ERROR: %s: invalid RMS wind speed error\n", argument); 213 | exit(1); 214 | } 215 | } 216 | 217 | { 218 | int year, month, day, hour, minute, second; 219 | year = iniparser_getint(scenario, "launch-time:year", -1); 220 | month = iniparser_getint(scenario, "launch-time:month", -1); 221 | day = iniparser_getint(scenario, "launch-time:day", -1); 222 | hour = iniparser_getint(scenario, "launch-time:hour", -1); 223 | minute = iniparser_getint(scenario, "launch-time:minute", -1); 224 | second = iniparser_getint(scenario, "launch-time:second", -1); 225 | 226 | if((year >= 0) && (month >= 0) && (day >= 0) && (hour >= 0) 227 | && (minute >= 0) && (second >= 0)) 228 | { 229 | struct tm timeval = { 0 }; 230 | time_t scenario_launch_time = -1; 231 | 232 | if(verbosity > 0) { 233 | fprintf(stderr, "INFO: Using launch time from scenario: " 234 | "%i/%i/%i %i:%i:%i\n", 235 | year, month, day, hour, minute, second); 236 | } 237 | 238 | timeval.tm_sec = second; 239 | timeval.tm_min = minute; 240 | timeval.tm_hour = hour; 241 | timeval.tm_mday = day; /* 1 - 31 */ 242 | timeval.tm_mon = month - 1; /* 0 - 11 */ 243 | timeval.tm_year = year - 1900; /* fuck you Millenium Bug! */ 244 | 245 | #ifndef _BSD_SOURCE 246 | # warning This version of mktime does not allow explicit setting of timezone. 247 | #else 248 | timeval.tm_zone = "UTC"; 249 | #endif 250 | 251 | scenario_launch_time = mktime(&timeval); 252 | if(scenario_launch_time <= 0) { 253 | fprintf(stderr, "ERROR: Launch time in scenario is invalid\n"); 254 | exit(1); 255 | } else { 256 | initial_timestamp = scenario_launch_time; 257 | } 258 | } 259 | } 260 | 261 | if(verbosity > 0) { 262 | fprintf(stderr, "INFO: Scenario loaded:\n"); 263 | fprintf(stderr, " - Initial latitude : %lf deg N\n", initial_lat); 264 | fprintf(stderr, " - Initial longitude : %lf deg E\n", initial_lng); 265 | fprintf(stderr, " - Initial altitude : %lf m above sea level\n", initial_alt); 266 | fprintf(stderr, " - Initial timestamp : %li\n", initial_timestamp); 267 | fprintf(stderr, " - Drag coeff. : %lf\n", drag_coeff); 268 | if(!descent_mode) { 269 | fprintf(stderr, " - Ascent rate : %lf m/s\n", ascent_rate); 270 | fprintf(stderr, " - Burst alt. : %lf m\n", burst_alt); 271 | } 272 | fprintf(stderr, " - Windspeed err. : %f m/s\n", rmswinderror); 273 | } 274 | 275 | { 276 | // do the actual stuff!! 277 | altitude_model_t* alt_model = altitude_model_new(descent_mode, burst_alt, 278 | ascent_rate, drag_coeff); 279 | if(!alt_model) { 280 | fprintf(stderr, "ERROR: error initialising altitude profile\n"); 281 | exit(1); 282 | } 283 | 284 | if (!run_model(file_cache, alt_model, 285 | initial_lat, initial_lng, initial_alt, initial_timestamp, 286 | rmswinderror)) { 287 | fprintf(stderr, "ERROR: error during model run!\n"); 288 | exit(1); 289 | } 290 | 291 | altitude_model_free(alt_model); 292 | } 293 | 294 | // release the scenario 295 | iniparser_freedict(scenario); 296 | 297 | // write footer to KML and close output files 298 | if (kml_file) { 299 | finish_kml(); 300 | fclose(kml_file); 301 | } 302 | 303 | if (output != stdout) { 304 | fclose(output); 305 | } 306 | } 307 | 308 | // release gopt data, 309 | gopt_free(options); 310 | 311 | // release the file cache resources. 312 | wind_file_cache_free(file_cache); 313 | 314 | return 0; 315 | } 316 | 317 | void write_position(float lat, float lng, float alt, int timestamp) { 318 | // the predictor uses 0<=lng<360; most other things expect -180 180) 320 | lng -= 360; 321 | 322 | if (kml_file) { 323 | fprintf(kml_file, "%g,%g,%g\n", lng, lat, alt); 324 | if (ferror(kml_file)) { 325 | fprintf(stderr, "ERROR: error writing to KML file\n"); 326 | exit(1); 327 | } 328 | } 329 | 330 | fprintf(output, "%d,%g,%g,%g\n", timestamp, lat, lng, alt); 331 | if (ferror(output)) { 332 | fprintf(stderr, "ERROR: error writing to CSV file\n"); 333 | exit(1); 334 | } 335 | } 336 | 337 | void start_kml() { 338 | FILE* kml_header; 339 | char c; 340 | 341 | kml_header = fopen("kml_header", "r"); 342 | 343 | while (!feof(kml_header)) { 344 | c = fgetc(kml_header); 345 | if (ferror(kml_header)) { 346 | fprintf(stderr, "ERROR: error reading KML header file\n"); 347 | exit(1); 348 | } 349 | if (!feof(kml_header)) fputc(c, kml_file); 350 | if (ferror(kml_file)) { 351 | fprintf(stderr, "ERROR: error writing header to KML file\n"); 352 | exit(1); 353 | } 354 | } 355 | 356 | fclose(kml_header); 357 | } 358 | 359 | void finish_kml() { 360 | FILE* kml_footer; 361 | char c; 362 | 363 | kml_footer = fopen("kml_footer", "r"); 364 | 365 | while (!feof(kml_footer)) { 366 | c = fgetc(kml_footer); 367 | if (ferror(kml_footer)) { 368 | fprintf(stderr, "ERROR: error reading KML footer file\n"); 369 | exit(1); 370 | } 371 | if (!feof(kml_footer)) fputc(c, kml_file); 372 | if (ferror(kml_file)) { 373 | fprintf(stderr, "ERROR: error writing footer to KML file\n"); 374 | exit(1); 375 | } 376 | } 377 | 378 | fclose(kml_footer); 379 | } 380 | 381 | --------------------------------------------------------------------------------