├── public ├── .DS_Store ├── images │ ├── glucosym_ui.png │ ├── glucosym_equations.png │ ├── glucosym_post_history.png │ └── glucosym_algo_pid_results.png ├── stylesheets │ └── style.css └── javascripts │ └── jquery-latest.min.js ├── views ├── error.jade ├── layout.jade └── index.jade ├── routes ├── users.js └── index.js ├── package.json ├── app.js ├── bin └── www ├── closed_loop_algorithm_samples ├── algo_pid.js └── algo_bw.js └── README.md /public/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Perceptus/GlucoSym/HEAD/public/.DS_Store -------------------------------------------------------------------------------- /public/images/glucosym_ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Perceptus/GlucoSym/HEAD/public/images/glucosym_ui.png -------------------------------------------------------------------------------- /public/images/glucosym_equations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Perceptus/GlucoSym/HEAD/public/images/glucosym_equations.png -------------------------------------------------------------------------------- /views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /public/images/glucosym_post_history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Perceptus/GlucoSym/HEAD/public/images/glucosym_post_history.png -------------------------------------------------------------------------------- /public/images/glucosym_algo_pid_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Perceptus/GlucoSym/HEAD/public/images/glucosym_algo_pid_results.png -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', function(req, res, next) { 6 | res.io.emit("socketToMe", "users"); 7 | res.send('respond with a resource.'); 8 | }); 9 | 10 | module.exports = router; 11 | -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | script(src="/javascripts/jquery-latest.min.js") 7 | script(src="https://www.google.com/jsapi?autoload={'modules':[{'name':'visualization','version':'1','packages':['corechart']}]}") 8 | body 9 | block content 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "www", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.13.2", 10 | "cookie-parser": "~1.3.5", 11 | "debug": "~2.6.9", 12 | "express": "~4.13.1", 13 | "jade": "~1.11.0", 14 | "jstat": "^1.6.1", 15 | "morgan": ">=1.9.1", 16 | "request": "^2.81.0", 17 | "serve-favicon": "~2.3.0", 18 | "socket.io": ">=2.4.0", 19 | "sync-request": "^4.0.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var routes = require('./routes/index'); 9 | var users = require('./routes/users'); 10 | 11 | var app = express(); 12 | var server = require('http').Server(app); 13 | var io = require('socket.io')(server); 14 | 15 | // view engine setup 16 | app.set('views', path.join(__dirname, 'views')); 17 | app.set('view engine', 'jade'); 18 | 19 | // uncomment after placing your favicon in /public 20 | //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); 21 | app.use(function(req, res, next){ 22 | res.io = io; 23 | next(); 24 | }); 25 | app.use(logger('dev')); 26 | app.use(bodyParser.json()); 27 | app.use(bodyParser.urlencoded({ extended: false })); 28 | app.use(cookieParser()); 29 | app.use(express.static(path.join(__dirname, 'public'))); 30 | 31 | app.use('/', routes); 32 | app.use('/users', users); 33 | 34 | // catch 404 and forward to error handler 35 | app.use(function(req, res, next) { 36 | var err = new Error('Not Found'); 37 | err.status = 404; 38 | next(err); 39 | }); 40 | 41 | // error handlers 42 | 43 | // development error handler 44 | // will print stacktrace 45 | if (app.get('env') === 'development') { 46 | app.use(function(err, req, res, next) { 47 | res.status(err.status || 500); 48 | res.render('error', { 49 | message: err.message, 50 | error: err 51 | }); 52 | }); 53 | } 54 | 55 | // production error handler 56 | // no stacktraces leaked to user 57 | app.use(function(err, req, res, next) { 58 | res.status(err.status || 500); 59 | res.render('error', { 60 | message: err.message, 61 | error: {} 62 | }); 63 | }); 64 | 65 | 66 | module.exports = {app: app, server: server}; 67 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app').app; 8 | var debug = require('debug')('www:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = require('../app').server; 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /closed_loop_algorithm_samples/algo_pid.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var request = require("sync-request"); 3 | var micro=0.000001; 4 | var obj = {"pid":{"Kp":.00888,"Ti":100,"Td":90},"sim_toggle":{"P":true,"I":true,"D":true},"BGTarget":120,"dt":5.0,"time":1800,"bioavail":6.0,"Vg":253.0,"IRss":1.3,"events":{"bolus":[{ "amt": 0.0, "start": 60 }, { "amt": 0.0, "start": 10 }],"basal":[{ "amt": 1.3, "start": 0,"length":0}],"carb":[{"amt":0.0,"start":600,"length":90},{"amt":0.0,"start":60,"length":90}]}}; 5 | 6 | function uconvert_basal(u) { 7 | return u / micro / 60.0; 8 | } 9 | 10 | var url='http://localhost:3000/dose'; 11 | var urldata='http://localhost:3000/'; 12 | var G = []; // glucose in units of mg/dl 13 | var dose=[]; 14 | var ID=[]; //units of microU/min 15 | var P=[]; 16 | var I=[]; 17 | var D=[]; 18 | var events=obj.events; 19 | dt = obj.dt; 20 | var time=obj.time; //min - simulation time goes from 0 to time increments of dt 21 | var n = Math.round(time / dt); 22 | var doses={bolus:[],basal:[]}; 23 | doses.bolus=new Array(n).fill(0); 24 | doses.basal=new Array(n).fill(0); 25 | 26 | ID[0]=obj.IRss/60.0*dt; //this needs to be set by user read from sim data 27 | 28 | for (var i = 0; i < events.bolus.length; i++) { 29 | doses.bolus[Math.round(events.bolus[i].start/dt)]=doses.basal[Math.round(events.bolus[i].start/dt)]+events.bolus[i].amt; 30 | } 31 | 32 | for (var i = 0; i < events.basal.length; i++) { 33 | for(var j=Math.round(events.basal[i].start/dt);j1) {var dgdt=(G[index-1]-G[index-2])/dt;} else {var dgdt=0.0} 82 | P[index]=obj.pid.Kp*(G[index-1]-obj.BGTarget); 83 | I[index]=I[index-1]+P[index]/obj.pid.Ti; 84 | D[index]=obj.pid.Kp*obj.pid.Td*dgdt; 85 | dose=doses.bolus[index]+doses.basal[index]; 86 | 87 | if(dt*index>5){ 88 | if (obj.sim_toggle.P==true) {dose=dose+P[index];} 89 | if (obj.sim_toggle.I==true) {dose=dose+I[index];} 90 | if (obj.sim_toggle.D==true) {dose=dose+D[index];}} 91 | 92 | if (dose<0) {dose=0.0}; 93 | console.log("P "+P[index]+"I "+I[index]+"D "+D[index]) 94 | console.log("Dose "+dose); 95 | return dose; 96 | } -------------------------------------------------------------------------------- /closed_loop_algorithm_samples/algo_bw.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var request = require("sync-request"); 3 | var micro=0.000001; 4 | var obj = {"BGTarget":120,"sens":20,"deltat_v":20,"dia":4,"dt":5.0,"time":600,"bioavail":6.0,"Vg":253.0,"IRss":1.3,"events":{"bolus":[{ "amt": 0.0, "start": 60 }, { "amt": 0.0, "start": 10 }],"basal":[{ "amt": 1.3, "start": 0,"length":600}],"carb":[{"amt":0.0,"start":600,"length":90},{"amt":0.0,"start":60,"length":90}]}}; 5 | 6 | function uconvert_basal(u) { 7 | return u / micro / 60.0; 8 | } 9 | 10 | var url='http://localhost:3000/dose'; 11 | var urldata='http://localhost:3000/'; 12 | var G = []; // glucose in units of mg/dl 13 | var dose=[]; 14 | var ID=[]; //units of microU/min 15 | var cor=[]; 16 | var iob=[]; 17 | var corsave=[]; 18 | var events=obj.events; 19 | dt = obj.dt; 20 | var time=obj.time; //min - simulation time goes from 0 to time increments of dt 21 | var n = Math.round(time / dt); 22 | var doses={bolus:[],basal:[]}; 23 | doses.bolus=new Array(n).fill(0); 24 | doses.basal=new Array(n).fill(0); 25 | corsave=new Array(n).fill(0); 26 | 27 | ID[0]=uconvert_basal(obj.IRss); //this needs to be set by user read from sim data 28 | 29 | for (var i = 0; i < events.bolus.length; i++) { 30 | doses.bolus[Math.round(events.bolus[i].start/dt)]=doses.basal[Math.round(events.bolus[i].start/dt)]+events.bolus[i].amt; 31 | } 32 | 33 | for (var i = 0; i < events.basal.length; i++) { 34 | for(var j=Math.round(events.basal[i].start/dt);j1) { 95 | var dgdt=(G[index-1]-G[index-2])/dt; 96 | corcalc=(G[index-1]+dgdt*obj.deltat_v-obj.BGTarget)/obj.sens-iob[index] 97 | } else { 98 | var dgdt=0.0; 99 | corcalc=0; 100 | }; 101 | 102 | if (corcalc>0.0) { 103 | cor.push({time:index*dt,amount:corcalc}); 104 | console.log(cor[cor.length-1]); 105 | } else { 106 | corcalc=0.0 107 | } 108 | 109 | corsave[index]=corcalc; 110 | dose=corcalc; 111 | dose=dose+doses.bolus[index]+doses.basal[index]; 112 | if (dose<0) {dose=0.0}; 113 | return dose; 114 | } 115 | 116 | function actin (idur,m) { 117 | if (m>idur*60) { 118 | return 0.0 119 | } else if (idur==3.5) { 120 | return -0.0000000560934*Math.pow(m,4) + 0.0000451551*Math.pow(m,3) - 0.00927644*Math.pow(m,2) + 0.0*m + 100.0 121 | } else if (idur==3) { 122 | return -3.2e-7*Math.pow(m,4)+1.354e-4*Math.pow(m,3)-1.76e-2*Math.pow(m,2)+9.255e-2*m+99.951 123 | } else if (idur==4) { 124 | return -3.31e-8*Math.pow(m,4)+2.53e-5*Math.pow(m,3)-5.51e-3*Math.pow(m,2)-9.086e-2*m+99.95 125 | } else if (idur==5) { 126 | return -2.95e-8*Math.pow(m,4)+2.32e-5*Math.pow(m,3)-5.55e-3*Math.pow(m,2)+4.49e-2*m+99.3 127 | } else if (idur==6) { 128 | return -1.493e-8*Math.pow(m,4)+1.413e-5*Math.pow(m,3)-4.095e-3*Math.pow(m,2)+6.365e-2*m+99.7 129 | } else { 130 | return 1000.0; 131 | } 132 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GlucoSym 2 | 3 | GlucoSym is an open-source simulator aimed to help in the testing and development of insulin delivery automation systems. 4 | 5 | The simulator is based on the following equations: 6 | 7 | ![Glucosym Equations](public/images/glucosym_equations.png) 8 | 9 | It allows a developer to: 10 | 11 | - Compare multiple algorithms on a same patient. 12 | - Test on a patient with parameter variation during the day. 13 | - Test against a population of patients 14 | 15 | We hope GlucoSym will help develop faster, safer & more accessible software for the #wearenotwaiting community. 16 | 17 | Remember GlucoSym is a work-in-progress! 18 | 19 | ## Setting-up 20 | 21 | You will need [nodejs](https://nodejs.org) installed. If you haven't already done that head on over to [the nodejs website](https://nodejs.org) and install for your system. 22 | 23 | Once installed, clone the repository on your computer: 24 | 25 | ``` 26 | $ git clone https://github.com/perceptus/glucosym.git 27 | ``` 28 | 29 | Then, to install required dependencies run: 30 | 31 | ``` 32 | $ npm install 33 | ``` 34 | 35 | ## Starting GlucoSym 36 | 37 | GlucoSym runs as a server on your computer so you can interact with it on your browser and have your own algorithm post data to it to get blood glucose values from the simulated patient back. 38 | 39 | To start the server, open a terminal window and run: 40 | 41 | ``` 42 | $ npm start 43 | ``` 44 | 45 | If everything is installed correctly, you should see the following on the terminal: 46 | 47 | ``` 48 | > www@0.0.0 start /path_to/glucosym 49 | > node ./bin/www 50 | ``` 51 | 52 | Now open your browser and navigate to http://localhost:3000 you should see GlucoSym UI: 53 | 54 | ![Glucosym Ui](public/images/glucosym_ui.png) 55 | 56 | When starting GlucoSym, by default "Patient A" is selected and its parameters are loaded. You can go through each parameter and modify it as needed before starting the simulation. If during the simulation, you change any of the parameters, the simulation will reset and you will loose all simulation history. 57 | 58 | Once you change parameters, you are now ready to test! 59 | 60 | ## Setting up your algorithm and posting to GlucoSym 61 | 62 | We recommend you to treat GlucoSym as your blood glucose source on your algorithm. Right now, you probably get a blood glucose from a CGM or Nightscout. You will have to create a way to post data to this server in order to get a blood glucose value back. 63 | 64 | To start getting blood glucose values, you will have to post to GlucoSym /dose endpoint: 65 | 66 | ``` 67 | var url = 'http://localhost:3000/dose'; 68 | var request = require("sync-request"); 69 | postdata = { "dose": 0.0, "dt": 5, "index": 0, "time": 1080, "events": {"bolus": [{"amt": 0.0, "start": 60}], "basal": [{"amt": 1.3, "start": 0, "length": 600}], "carb": [{"amt": 0.0, "start": 600, "length": 90}]} }; 70 | request('POST', url, {json: postdata}); 71 | 72 | var response = JSON.parse(res3.getBody()); 73 | console.log(response.bg); 74 | ``` 75 | 76 | Here is a simple explanation of each of the post parameters: 77 | 78 | * dose: Insulin dose in Units given during the time-step. In the case of a basal adjustment you will need to calculate how much insulin will be given in the time-step defined by dt ( ie how many inulin units will be given in 5 minutes by the set basal profile or temp basal? ) 79 | * dt: Change in time each step in minutes 80 | * index: Current index from the start of the simulation, starting at 0 81 | * time: Total simulation run-time in minutes 82 | * events: You can set events so the simulator will consider them during the run. We recommend to send them on-the-go as your algo will probably be generating these on the run, specifically with the carb part of the events. If you decide to add carbs on-the-go, set the start time at 0 for the new carb being sent so 0 is the time at which the carb was sent to the simulator. 83 | 84 | ## Finishing the simulation 85 | 86 | As you start posting to the server, blood glucoses will be returned according to insulin and carb events being sent. Once you have finished running your simulation, you need to do a final post so the simulator can know the run has ended and it can graph the data that was being stored for the run. 87 | 88 | ``` 89 | var url = 'http://localhost:3000/'; 90 | var request = require("sync-request"); 91 | postdata = { "correction": [1,2,3,4,5,6,7,8,9,10], "iob": [1,2,3,4,5,6,7,8,9,10] }; 92 | request('POST', url, {json: postdata}); 93 | 94 | var response = JSON.parse(res3.getBody()); 95 | console.log(response); 96 | ``` 97 | 98 | In this case, you can send the simulator whatever you want it to graph along its own default values. Just as "correction" and "iob" was added to the post, you can add any array of data in the same format. 99 | 100 | Once the post is finished, you can go to the browser and the data will be graphed. 101 | 102 | MAKE SURE YOU HAVE AN ACTIVE INTERNET CONNECTION TO DISPLAY GRAPHS! ( See Issue #3 ) 103 | 104 | ## Algorithm samples 105 | 106 | We have included a copule of simple, not cleaned up code samples of algorithms that have been tested with the simulator. They are all located on the /closed_loop_algorithm_samples folder on the repository. 107 | 108 | To run the samples, first start the GlucoSym server and then: 109 | 110 | ``` 111 | $ cd closed_loop_algorithm_samples 112 | $ node algo_pid_.js 113 | ``` 114 | 115 | You should start seeing all of the post events on the terminal window where the server is running: 116 | 117 | ![Glucosym Post History](public/images/glucosym_post_history.png) 118 | 119 | Each script has a fixed run-time and it will post at the end so the data gets graphed on the simulator. 120 | 121 | When the simulation finishes, go to your browser widow where the sim is loaded and scroll to the bottom to see the results: 122 | 123 | ![Glucosym Algo Pid Results](public/images/glucosym_algo_pid_results.png) 124 | 125 | # PLEASE READ! 126 | 127 | GlucoSym was developed to be used as a tool to validate our work and help us advance faster into better insulin delivery automation solutions. 128 | 129 | Please refrain from using GlucoSym results to evidence "good" vs "bad" work from other people in the community! 130 | 131 | The MIT License (MIT) 132 | 133 | Copyright (c) 2017 Perceptus.org 134 | 135 | Permission is hereby granted, free of charge, to any person obtaining a copy 136 | of this software and associated documentation files (the "Software"), to deal 137 | in the Software without restriction, including without limitation the rights 138 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 139 | copies of the Software, and to permit persons to whom the Software is 140 | furnished to do so, subject to the following conditions: 141 | 142 | The above copyright notice and this permission notice shall be included in all 143 | copies or substantial portions of the Software. 144 | 145 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 146 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 147 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 148 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 149 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 150 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 151 | SOFTWARE. -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | // // // // // // // // // // // // // // // // // // // // // 2 | // EXPRESS FRAMEWORK 3 | // // // // // // // // // // // // // // // // // // // // // 4 | var express = require('express'); 5 | var router = express.Router(); 6 | var jStat=require('jstat').jStat; 7 | 8 | // // // // // // // // // // // // // // // // // // // // // 9 | // SIMULATOR VARIABLES 10 | // // // // // // // // // // // // // // // // // // // // // 11 | 12 | // Define Initial Patient Parameters ( Patient A ) 13 | var patient_parameters = { 14 | "sim_p": { 15 | "Si": [], // dl/microU Sensitivity factor array during simulation run 16 | "bw": 89, // KG Body Weight 17 | "Ci": 20.1, // dl/min Insulin Clearance 18 | "tau1": 49, // min Time constant associated with insulin movement between the SC delivery site and plasma 19 | "tau2": 47, // min Time constant associated with insulin movement between the SC delivery site and plasma 20 | "Vg": 253, // dl Distribution volume in which glucose equilibrates. 21 | "p2": 0.0106, // 1/min Delay in insulin action following an increase in plasma insulin 22 | "EGP": 1.33, // mg/dl/min Endogenous glucose production rate that would be estimated at zero insulin 23 | "GEZI": 0.0022, // 1/min Effect of glucose per se to increase glucose uptake into cells and lower endogenous glucose production at zero insulin 24 | "Si_base": 0.00000811, // dl/microU Baseline Sensitivity Factor ( Initial Sensitivity ) 25 | "bioavail": 6.0, // ADD UNITS ADD DESCRIPTION 26 | "initialglucose": 100 // mg/dl Simulated patient initial blood glucose level 27 | }, 28 | "dt": 5.0, // min Time between simulator steps 29 | "time": 2160, // min Total simulation run time 30 | "sens": [ { 31 | "per": 100, // % Sensitivity variation at point in time. Per is percentage to change at indicated time. 32 | "start": 0 // min Sensitivity variation at point in time. Start is start time for variation in minutes. 33 | } ] 34 | }; 35 | 36 | // Initialize simulator variables 37 | var micro = 0.000001; 38 | var params = { sens:[] }; 39 | var ID = []; 40 | var Isc = []; 41 | var Ip = []; 42 | var Ieff = []; 43 | var Ra = []; 44 | var G = []; 45 | var dt = patient_parameters.dt; 46 | var time = patient_parameters.time; 47 | var sim_p = patient_parameters.sim_p; 48 | 49 | // // // // // // // // // // // // // // // // // // // // // 50 | // SIMULATOR MATH 51 | // // // // // // // // // // // // // // // // // // // // // 52 | 53 | // Each time step, new parameters should be calculated with this function 54 | function simulated_patient_step(sim_p, ID, Ra, dt, index, Isc, Ip, Ieff, G) { 55 | Iscnew = Isc + dt * (-1.0 / sim_p.tau1 * Isc + 1.0 / sim_p.tau1 * ID / sim_p.Ci); 56 | Ipnew = Ip + dt * (-1.0 / sim_p.tau2 * Ip + 1.0 / sim_p.tau2 * Isc); 57 | Ieffnew = Ieff + dt * (-sim_p.p2 * Ieff + sim_p.p2 * sim_p.Si[index] * Ip); 58 | Gnew = G + dt * (-(sim_p.GEZI + Ieff) * G + sim_p.EGP + Ra/sim_p.Vg); 59 | return [Iscnew, Ipnew, Ieffnew, Gnew]; 60 | } 61 | 62 | // This function should get called when adding carbs at the simulation start or during the simulation 63 | function add_carbs(carbs,index) { 64 | for (var i = 0; i < carbs.length; i++) { 65 | for(var j=Math.round((carbs[i].start+(index*dt))/dt);jhighlimit ) { 102 | highcount++; 103 | } else { 104 | incount++; 105 | } 106 | } 107 | 108 | stats.inrange=incount/G.length*100.0; 109 | stats.lowrange=lowcount/G.length*100.0; 110 | stats.highrange=highcount/G.length*100.0 111 | 112 | return stats 113 | } 114 | 115 | // // // // // // // // // // // // // // // // // // // // // 116 | // SIMULATOR BROWSER ROUTES ( ie http://localhost:3000 ) 117 | // // // // // // // // // // // // // // // // // // // // // 118 | 119 | // This should display the simulator UI where you can select & customize a patient. 120 | router.get('/', function(req, res, next) { 121 | res.render('index', { title: 'GlucoSym', patient_parameters: patient_parameters }); 122 | }); 123 | 124 | // This function should get called at the end of a simulator run to display graphs of the whole run as well as custom data sent by your algorithm. This funciton should also reset the run for a new start. 125 | router.post('/', function(req, res, next) { 126 | var stats=simulation_statistics(G); 127 | var IDU=[]; 128 | for (var i=0;i0) { 169 | 170 | add_carbs(post.events.carb,index); 171 | result = simulated_patient_step(sim_p, ID[index], Ra[index], dt, index, Isc[index-1], Ip[index-1], Ieff[index-1], G[index-1]); 172 | Isc[index]=result[0]; 173 | Ip[index]=result[1]; 174 | Ieff[index]=result[2]; 175 | G[index]=result[3]; 176 | 177 | } else { 178 | 179 | var events=post.events; 180 | n = Math.round(time / dt); 181 | Ra = new Array(n).fill(0); 182 | add_carbs(events.carb,index); 183 | sim_p.Si=new Array(n+1).fill(sim_p.Si_base); 184 | 185 | for (var index = 0; index < params.sens.length; index++) { 186 | for(var j=Math.round(params.sens[index].start/dt);j