Compare commits
25 Commits
Author | SHA1 | Date |
---|---|---|
Robert Chen | 59b74b168d | 11 years ago |
Scott Mobley | dd39c692d4 | 11 years ago |
Scott Mobley | 97fc37f40c | 11 years ago |
Robert Chen | ac516d2b27 | 11 years ago |
Zott820 | fa3def9dbd | 11 years ago |
Zott820 | 7963ffee91 | 11 years ago |
Scott Mobley | 59532c039f | 11 years ago |
Scott Mobley | 198f7e97a9 | 11 years ago |
Scott Mobley | 167e8f7b41 | 11 years ago |
Scott Mobley | 6b0dffde85 | 11 years ago |
Scott Mobley | 6630f8e84f | 11 years ago |
Scott Mobley | 44e4056aaf | 11 years ago |
Scott Mobley | 9275d260fa | 11 years ago |
Scott Mobley | 925ca23bae | 11 years ago |
Zott820 | b5dc2bc8bd | 11 years ago |
Zott820 | 302bc36260 | 11 years ago |
Scott Mobley | b87537c250 | 11 years ago |
Scott Mobley | 40b2667779 | 11 years ago |
Scott Mobley | b5f88a27d7 | 11 years ago |
Scott Mobley | 725b0cd5a9 | 11 years ago |
Scott Mobley | 091ac61034 | 11 years ago |
Scott Mobley | f67a7410e9 | 11 years ago |
Scott Mobley | bf3bc66a48 | 11 years ago |
Scott Mobley | 654da49e2d | 11 years ago |
Scott Mobley | 5d72b59901 | 11 years ago |
54 changed files with 741 additions and 43 deletions
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
Unless Otherwise noted, all sounds and music are in the public Domain. |
||||
|
||||
--------------------------- |
||||
|
||||
Main Menu Music |
||||
turkey_in_the_straw.mp3 |
||||
|
||||
Composition Licence: Traditional, arr. first published between 1829 and 1834. This MP3 (or other media file) is in the public domain because its copyright has expired. This applies to the United States, where Works published prior to 1978 were copyright protected for a maximum of 75 years. See Circular 1 "COPYRIGHT BASICS" from the U.S. Copyright Office. Works published before 1924 are now in the public domain. |
||||
|
||||
This file is also in the public domain in countries that figure copyright from the date of death of the artist (post mortem auctoris in this case George Washington Dixon 1801? âMarch 2, 1861), and that most commonly runs for a period of 50 to 70 years from December 31 of that year. |
||||
|
||||
Performance Licence: The United States Air Force Band Web site is provided as a public service by the United States Air Force Band and Department of the Air Force. |
||||
|
||||
Information presented on the United States Air Force Band site is considered public information and may be distributed or copied. Use of appropriate byline/photo/image credits is requested. |
||||
|
||||
UPLOADED by Sookietex |
||||
|
||||
--------------------------- |
||||
|
||||
freesound.org |
||||
Listed as file number, author, and title |
||||
|
||||
|
||||
32371__walter-odington__frying-pan-chicken-and-onion |
||||
36328__unclesigmund__coinbank - Crank Sound |
||||
63531__florian-reinke__click1 |
||||
63532__florian-reinke__click2 |
||||
72565__wrinex__supermarkt-ms |
||||
73261__jakobhandersen__click |
||||
87564__sgak__fire |
||||
142895__ceremonialchapstick__winchester-30-30-lever-action-eject-1 |
||||
155441__wakey__buzz1 |
||||
175678__saundklyp__tongue-click |
||||
184438__capslok__cash-register-fake |
||||
|
||||
|
||||
--------------------------- |
||||
|
||||
Store Background Music: |
||||
|
||||
Title: Waterford |
||||
Artist: Kevin MacLeod |
||||
Genre: World |
||||
Length: 1:54 |
||||
Bitrate: 286 kbps |
||||
Size: 3.88MB |
||||
|
||||
Simple folk-inspired ditty. Source: Kevin MacLeod |
||||
|
||||
|
||||
|
||||
--------------------------- |
||||
|
||||
|
||||
|
||||
Unused Music (Don't Cite this...yet) |
||||
----------------------------------------------------------------------------------------------------------- |
||||
|
||||
Title: Improved Ice Cream Truck |
||||
Artist: Kevin MacLeod |
||||
Genre: Folk |
||||
Length: 0:51 |
||||
Bitrate: 320 kbps |
||||
Size: 1.93MB |
||||
|
||||
An improved version of a traditional Ice Cream Truck melody… whose actual title I forget. |
||||
|
||||
--------------------------- |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 617 KiB |
Binary file not shown.
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
function findClosest(value,array) { |
||||
closestDiff = null; |
||||
closestPosition = null; |
||||
for (var i=0;i<array.length;i++) { |
||||
diff = Math.abs(value-array[i]) |
||||
if (diff<closestDiff || closestDiff == null) { |
||||
closestPosition=i; |
||||
closestDiff = diff; |
||||
}
|
||||
} |
||||
return ([closestPosition,array[closestPosition]]) |
||||
} |
||||
|
||||
function biotSphereCoefficients (Biot) { |
||||
Bi = [0.01, 0.02, 0.04, 0.06, 0.08, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100, 10000] |
||||
lambdaOne = [0.1730, 0.2445, 0.3450, 0.4217, 0.4860, 0.5423, 0.7593, 0.9208, 1.0528, 1.1656, 1.2644, 1.3525, 1.4320, 1.5044, 1.5708, 2.0288, 2.2889, 2.4556, 2.5704, 2.6537, 2.7165, 2.7654, 2.8044, 2.8363, 2.9857, 3.3072, 3.0632, 3.0788, 3.1102, 3.1416] |
||||
alphaOne = [ 1.0030, 1.0060, 1.0120, 1.0179, 1.0239, 1.0298, 1.0592, 1.0880, 1.1164, 1.1441, 1.1713, 1.1978, 1.2236, 1.2488, 1.2732, 1.4793, 1.6227, 1.7202, 1.7870, 1.8338, 1.8673, 1.8920, 1.9106, 1.9249, 1.9781, 1.9898, 1.9942, 1.9962, 1.9990, 2 ] |
||||
position = findClosest(Biot,Bi)[0] |
||||
return([lambdaOne[position], alphaOne[position]]) |
||||
} |
||||
|
||||
function lumpedCapacitance (outerRadius,radiusInner,tempInitial,tempInfini,heatConvectionTerm,t) { |
||||
volume = (4/3)*Math.PI*Math.pow(outerRadius,3) - (4/3)*Math.PI*Math.pow(radiusInner,3); //3D Sphere
|
||||
surfaceArea = 4*Math.PI*Math.pow(outerRadius,2); //3D Sphere
|
||||
mass = density * volume; |
||||
charLength = volume/surfaceArea ; |
||||
biotNum = heatConvectionTerm * charLength/thermalConduct |
||||
console.log("The Biot Value is " + biotNum) |
||||
b=(heatConvectionTerm)/(density*charLength*cp) |
||||
console.log("The time constant b is "+ b) |
||||
tempAtTime = Math.exp(-b*t)*(tempInitial-tempInfini)+tempInfini; |
||||
console.log("The Temperature at time " + t +" seconds is " + tempAtTime) |
||||
qDot = -1*heatConvectionTerm*surfaceArea*(tempAtTime-tempInfini) //Heat Transfer Rate Useful for water Loss
|
||||
console.log("The Heat Flux is " + qDot ) |
||||
return([tempAtTime,qDot]) |
||||
} |
||||
|
||||
function transientSphereOneTerm (rPosition,rTotal,tempInitial,tempInfini,t) { |
||||
alpha = thermalConduct/(density*cp) |
||||
console.log("Alpha is " + alpha) |
||||
|
||||
Fourier = (alpha*t)/Math.pow(rTotal,2) |
||||
console.log("Fourier is " + Fourier) |
||||
biotNum = heatConvection * rTotal/thermalConduct |
||||
console.log("The Biot Value is " + biotNum) |
||||
temp=biotSphereCoefficients(biotNum) |
||||
lambdaOne=temp[0]; |
||||
alphaOne=temp[1]; |
||||
console.log("lambda1 is " + lambdaOne) |
||||
console.log("A1 is " + alphaOne) |
||||
|
||||
//This is only valid for Fourier greater than 0.2
|
||||
sinPortion= Math.sin(lambdaOne*rPosition/rTotal)/(lambdaOne*rPosition/rTotal); |
||||
expotentialPortion = alphaOne*(1/Math.exp(Math.pow(lambdaOne,2)*Fourier)) |
||||
tempAtTimeAndRadius=(sinPortion*expotentialPortion*(tempInitial-tempInfini))+tempInfini |
||||
console.log("The Temperature at radius " + rPosition + " m and time " + t + " seconds is " + tempAtTimeAndRadius + " C or " + celsiusToFarenheit(tempAtTimeAndRadius) + " F"); |
||||
} |
||||
|
||||
function lumpedCapacitanceMethod (outerRadius,radiusInner,tempInitial,tempInfini, t) { |
||||
volume = (4/3)*Math.PI*Math.pow(outerRadius,3) - (4/3)*Math.PI*Math.pow(radiusInner,3); //3D Sphere
|
||||
surfaceArea = 4*Math.PI*Math.pow(outerRadius,2); //3D Sphere
|
||||
mass = density * volume; |
||||
charLength = volume/surfaceArea ; |
||||
biotNum = heatConvection * charLength/thermalConduct |
||||
console.log("The Biot Value is " + biotNum) |
||||
b=(heatConvection)/(density*charLength*cp) |
||||
console.log("The time constant b is "+ b) |
||||
tempAtTime = Math.exp(-b*t)*(tempInitial-tempInfini)+tempInfini; |
||||
console.log("The Temperature at time " + t +" seconds is " + tempAtTime) |
||||
Qdot = -1*heatConvection*surfaceArea*(tempAtTime-tempInfini) //Heat Transfer Rate Useful for water Loss
|
||||
console.log("The Heat Flux is " + Qdot ) |
||||
return(tempAtTime) |
||||
} |
||||
|
||||
|
||||
function findAllRoots (Biot) { |
||||
limit = 50; //Terms to Compute too
|
||||
storage = []; |
||||
for (var k=0; k<=limit; k++) { |
||||
minK = (k+0.5)*Math.PI; |
||||
maxK = (k+1)*Math.PI; |
||||
answer = bisectionMethod(minK,maxK,Biot); |
||||
if (answer !=undefined) { |
||||
storage.push(answer); |
||||
} |
||||
} |
||||
//console.log(storage)
|
||||
return(storage) |
||||
} |
@ -0,0 +1,263 @@
@@ -0,0 +1,263 @@
|
||||
//Useful Websites
|
||||
// http://math.stackexchange.com/questions/406082/numerical-method-to-solve-a-trigonometric-cotangent-function-transient-heat
|
||||
// http://web.cecs.pdx.edu/~gerry/epub/pdf/transientConductionSphere.pdf
|
||||
// http://web.cecs.pdx.edu/~gerry/epub/
|
||||
//http://highered.mcgraw-hill.com/sites/dl/free/0073129305/314124/cen29305_ch04.pdf
|
||||
//http://www.nt.ntnu.no/users/skoge/prost/proceedings/aiche-2005/topical/pdffiles/T9/papers/554a.pdf
|
||||
//http://www.rohanliston.com/portfolio/just-for-fun?id=23
|
||||
|
||||
//Global Variables for Turkey
|
||||
density = 1050; // kg/m3 Assuming Density of Water 1000 kg/m3
|
||||
cp = 2000 // 2810 J/kg K for Turkey. Extra is to semi-account for water evaporation energy
|
||||
heatConvection = 9; // W/m2 K Some Reasonable estimate for natural Convection. Change as needed. 5-25
|
||||
thermalConduct = 0.412 // W/m K // Chicken
|
||||
globalTime = 0; |
||||
|
||||
function celsiusToFarenheit(celsius) { |
||||
farenheit = (celsius*(9/5)) + 32; |
||||
return(farenheit) |
||||
} |
||||
|
||||
function farenheitToCelsius (farenheit) { |
||||
celsius = (farenheit*(9/5)) + 32; |
||||
return(celsius) |
||||
} |
||||
|
||||
function poundsToKilograms(pounds) { |
||||
kilograms = (pounds * 0.453592); |
||||
return(kilograms) |
||||
} |
||||
|
||||
function calculateRadius(weight) { |
||||
//Using Ratios for a rectangular Box Turkey
|
||||
ratioLvG=1.4; //1.4, Turkey length vs shoulder girth
|
||||
ratioLvH=2; //2, Turkey length vs height from resting position
|
||||
|
||||
length = Math.pow(weight/((1/ratioLvG)*(1/ratioLvH)*density),(1/3)) |
||||
depth = 1/(ratioLvG /length); |
||||
height = 1/(ratioLvH /length); |
||||
simpleRadius = length/2; //Doesn't take into account equal Volume
|
||||
|
||||
rectangleVolume = depth*height*length; //m^3 Multiple by 1/3 to account for triangular shape and empty Space
|
||||
complexRadius = Math.pow(rectangleVolume/((4/3)*Math.PI), 1/3); //Volume of 3D Box = 3D Sphere
|
||||
|
||||
console.log("Simple Radius " + simpleRadius + " Meters") |
||||
console.log("Complex Radius " + complexRadius + " Meters") |
||||
return(complexRadius) |
||||
} |
||||
|
||||
|
||||
function findAllRoots(min,max,splitsNum,Biot) { |
||||
var step = ( max - min ) / ( splitsNum - 1 ); |
||||
var storage = []; |
||||
for (var i = step; i < max; i=i+step ) { |
||||
negativeTest = lambdaFormula(i-step, Biot)*lambdaFormula(i, Biot); |
||||
if (negativeTest <= 0) { |
||||
answer = bisectionMethod(i-step,i,Biot); |
||||
if (answer !=undefined) { |
||||
storage.push(answer); |
||||
} |
||||
} |
||||
else { |
||||
//console.log("No Bracketed Root " + negativeTest)
|
||||
} |
||||
} |
||||
return(storage)
|
||||
} |
||||
|
||||
|
||||
function bisectionMethod(min,max,Biot) { |
||||
errorTolerance = (1/Math.pow(10,8)) |
||||
result = Infinity // some large value to ensure the calculation goes through.
|
||||
negativeTest =lambdaFormula(min, Biot)*lambdaFormula(max, Biot) |
||||
if (negativeTest <=0 ) { |
||||
var antiFreeze=0; |
||||
while (Math.abs(result) > errorTolerance && antiFreeze<=500) { //The greater the antiFreeze, the more wasted cycles around a singularity
|
||||
lambdaN = (min+max)/2 |
||||
result=lambdaFormula(lambdaN, Biot) |
||||
if (Math.abs(result) <= errorTolerance && result<=errorTolerance) { |
||||
return (lambdaN); //At Root
|
||||
} |
||||
else if ((lambdaFormula(min, Biot)*lambdaFormula(lambdaN, Biot))>=0) { |
||||
min=lambdaN; |
||||
} |
||||
else if ((lambdaFormula(max, Biot)*lambdaFormula(lambdaN, Biot))>=0) { |
||||
max=lambdaN; |
||||
} |
||||
antiFreeze++ |
||||
} |
||||
} |
||||
} |
||||
|
||||
function lambdaFormula(lambdaN, Biot) {
|
||||
result = 1-lambdaN*(1/Math.tan(lambdaN))-Biot; |
||||
return(result) |
||||
} |
||||
|
||||
function oven() { |
||||
this.tempInfini=20; //C
|
||||
this.setTemp = 20; |
||||
this.steadyTemp = 20; |
||||
this.steadyTimer = 0; |
||||
var proportional = 0.004; // This value is arbitrary to how fast you want the temperatures to converge. (Or oscillate, which could be realistic as well)
|
||||
var errorTolerance = 10; //Stove is accurate to 25 degree Celcius Should hopefully oscillate below that value.
|
||||
|
||||
this.changeTemp = function(setTemp) { |
||||
this.setTemp = setTemp; |
||||
} |
||||
|
||||
this.equalizeTemp = function() { // Equalize Temp will need to be sent each time iteration
|
||||
var error = Math.abs(this.setTemp-this.tempInfini); |
||||
if (this.setTemp>this.tempInfini) { |
||||
this.tempInfini = this.tempInfini + error*proportional; |
||||
} |
||||
else if (this.setTemp<this.tempInfini) { |
||||
this.tempInfini = this.tempInfini - error*proportional; |
||||
} |
||||
if (error>errorTolerance) { |
||||
return (true) //Need to run the Heat Calculations again next cycle
|
||||
} |
||||
else { |
||||
this.steadyTemp = this.tempInfini; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function layerModel(name,radiusPercentage) { |
||||
this.name = name; |
||||
this.radiusPercent=radiusPercentage; |
||||
this.initialTemp = 20; |
||||
this.waterLost = 0; |
||||
this.finalTemperature = 20; |
||||
} |
||||
|
||||
|
||||
function turkeyModel(weight) { |
||||
this.totalRadius = calculateRadius(weight) |
||||
this.skin = new layerModel("Skin",0.85) |
||||
this.body = new layerModel("Body",0.45) |
||||
this.core = new layerModel("Core",0.01) |
||||
|
||||
this.cookCondition = function(cookValue) { |
||||
var multiplier = 1; |
||||
if (cookValue>=multiplier*250000) { |
||||
return("House Fire") |
||||
} |
||||
else if(cookValue>=multiplier*150000) { |
||||
return("Charcoal") |
||||
} |
||||
else if (cookValue>=multiplier*80000) { |
||||
return("Dry") |
||||
} |
||||
else if (cookValue>=multiplier*18000) { |
||||
return("Cooked") |
||||
}
|
||||
else if (cookValue>=multiplier*5000) { |
||||
return("Undercooked") |
||||
}
|
||||
else { |
||||
return("Raw") |
||||
}
|
||||
} |
||||
|
||||
this.resetLayerTemps = function () { |
||||
this.skin.initialTemp = this.skin.finalTemperature; |
||||
this.body.initialTemp = this.body.finalTemperature; |
||||
this.core.initialTemp = this.core.finalTemperature; |
||||
} |
||||
|
||||
this.updateLayerTemps = function() { |
||||
//Temperature Calculations
|
||||
this.skin.finalTemperature = transientSphereSeries (this.skin.radiusPercent*this.totalRadius,this.totalRadius,this.skin.initialTemp,ovenObject.steadyTemp,globalTime) |
||||
this.body.finalTemperature = transientSphereSeries (this.body.radiusPercent*this.totalRadius,this.totalRadius,this.body.initialTemp,ovenObject.steadyTemp,globalTime) |
||||
this.core.finalTemperature = transientSphereSeries (this.core.radiusPercent*this.totalRadius,this.totalRadius,this.core.initialTemp,ovenObject.steadyTemp,globalTime) |
||||
|
||||
//Water Loss Calculations
|
||||
this.skin.waterLost = this.skin.waterLost + waterLoss(this.skin.finalTemperature) |
||||
this.body.waterLost = this.body.waterLost + waterLoss(this.body.finalTemperature) |
||||
this.core.waterLost = this.core.waterLost + waterLoss(this.core.finalTemperature) |
||||
|
||||
console.log("Skin: "+ this.skin.waterLost + " " + turkey.cookCondition(this.skin.waterLost)); |
||||
console.log("Body: "+ this.body.waterLost + " " + turkey.cookCondition(this.body.waterLost)); |
||||
console.log("Core: "+ this.core.waterLost + " " + turkey.cookCondition(this.core.waterLost)); |
||||
} |
||||
} |
||||
|
||||
function sphereVolume (radius) { |
||||
return((4/3)*Math.PI*Math.pow(radius,3)) |
||||
} |
||||
|
||||
function sphereSurfaceArea(radius) { |
||||
return (4*Math.PI*Math.pow(radius,2)) |
||||
} |
||||
|
||||
function waterLoss(temperature) { |
||||
return (Math.pow(10,(temperature-20)/80)-1) |
||||
} |
||||
|
||||
var oldBiot=null; |
||||
function transientSphereSeries (rPosition,rTotal,tempInitial,tempInfini,t) { |
||||
var min = 0; |
||||
var max = 10000; // This are for setting Lambda boundries and nothing else
|
||||
//thermalConduct = ((tempInitial-20)*(0.13/60)) + 0.32;
|
||||
var sum=0; |
||||
var alpha = thermalConduct/(density*cp) |
||||
//console.log("Alpha is " + alpha)
|
||||
|
||||
var Fourier = (alpha*t)/Math.pow(rTotal,2) |
||||
//console.log("Fourier is " + Fourier)
|
||||
|
||||
var biotNum = heatConvection * rTotal/thermalConduct |
||||
|
||||
if (biotNum != oldBiot) { |
||||
console.log("Recalculating Lambda Terms") |
||||
lambdaTerms = findAllRoots(min,max,max*Math.PI*10,biotNum) |
||||
oldBiot = biotNum; |
||||
} |
||||
|
||||
//console.log("The Biot Value is " + biotNum)
|
||||
|
||||
for (var i = 0; i<lambdaTerms.length; i++) { |
||||
lambdaN = lambdaTerms[i] |
||||
sinPortion= Math.sin(lambdaN*rPosition/rTotal)/(lambdaN*rPosition/rTotal); |
||||
exponentialPortion = (1/Math.exp(Math.pow(lambdaN,2)*Fourier)) |
||||
frontCoefficientPortion = 4*(Math.sin(lambdaN)-(lambdaN*Math.cos(lambdaN)))/ (2*lambdaN-Math.sin(2*lambdaN)) |
||||
sum = frontCoefficientPortion*exponentialPortion*sinPortion + sum |
||||
} |
||||
tempAtTimeAndRadius=(sum*(tempInitial-tempInfini))+tempInfini |
||||
|
||||
console.log("The Temperature at radius " + rPosition + " m and time " + t/60/60 + " hours is " + tempAtTimeAndRadius + " C or " + celsiusToFarenheit(tempAtTimeAndRadius) + " F"); |
||||
return(tempAtTimeAndRadius) |
||||
} |
||||
|
||||
|
||||
//Running the Program Stuff
|
||||
|
||||
ovenObject = new oven (); |
||||
turkey = new turkeyModel(7.257); |
||||
|
||||
setInterval(function(){time()},1000); |
||||
|
||||
totalCookTime = 0; |
||||
scale = 1 |
||||
function time() { |
||||
console.clear() |
||||
var equalized = ovenObject.equalizeTemp() |
||||
if (ovenObject.steadyTimer>=80*scale && equalized) { |
||||
ovenObject.steadyTimer = 0; |
||||
ovenObject.steadyTemp = ovenObject.tempInfini |
||||
turkey.resetLayerTemps(); |
||||
globalTime = 0; //Reset the model's time calculation if there are major changes in the tolerance of the temperature
|
||||
} |
||||
else { |
||||
globalTime = globalTime + scale; |
||||
ovenObject.steadyTimer = ovenObject.steadyTimer + scale; |
||||
totalCookTime = totalCookTime + scale; |
||||
} |
||||
|
||||
console.log(ovenObject.tempInfini) |
||||
console.log(ovenObject.steadyTemp) |
||||
console.log(ovenObject.steadyTimer) |
||||
console.log(totalCookTime/60/60 +" hours") |
||||
turkey.updateLayerTemps(); |
||||
} |
@ -0,0 +1,289 @@
@@ -0,0 +1,289 @@
|
||||
function TurkeyLayer( name, layerNumber1, turkeyModel, ovenModel ){ |
||||
var that = this; |
||||
|
||||
this.name = name; |
||||
this.layerNumber=layerNumber1; |
||||
this.waterLost = 0; |
||||
this.finalTemperature = 20; |
||||
this.cookCondition = "Raw"; |
||||
|
||||
return { |
||||
updateTemperatureTick: function(){ |
||||
that.finalTemperature = turkeyModel.globTemp[that.layerNumber] |
||||
console.log(turkeyModel.globTemp) |
||||
that.waterLost = that.waterLost + UtilityFunctions.waterLoss( that.finalTemperature ); |
||||
that.cookCondition = UtilityFunctions.cookCondition(that.waterLost); |
||||
if(DEBUG) console.log( that.name + ": "+ that.waterLost + " " + that.cookCondition + " " + that.finalTemperature + " C" ); |
||||
}, |
||||
getCondition: function(){ |
||||
return that.cookCondition; |
||||
}, |
||||
getTemperature: function(){ |
||||
return that.finalTemperature; |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
|
||||
function TurkeyModel( weight, ovenModel ){ |
||||
this.density = 700; // kg/m3 Assuming Density of Water 1000 kg/m3
|
||||
this.cp = 2810; // 2810 J/kg K for Turkey. Extra is to semi-account for water evaporation energy
|
||||
this.heatConvection = 9; // W/m2 K Some Reasonable estimate for natural Convection. Change as needed. 5-25
|
||||
this.thermalConduct = 0.412; // W/m K // Chicken
|
||||
this.skin = {}; |
||||
this.body = {}; |
||||
this.core = {}; |
||||
this.splitsNum = 20; |
||||
console.log(UtilityFunctions.lbs2kgs(weight)) |
||||
this.totalRadius = UtilityFunctions.calculateRadius( UtilityFunctions.lbs2kgs(weight), this.density ); |
||||
|
||||
|
||||
this.totalLayers = [ new TurkeyLayer("Skin", this.splitsNum-1, this, ovenModel ), |
||||
new TurkeyLayer("Body", this.splitsNum-4, this, ovenModel ), |
||||
new TurkeyLayer("Core", 0, this, ovenModel ) ]; |
||||
|
||||
// Whenever temperature is changed
|
||||
this.updateLayerTemps = function() { |
||||
this.globTemp = UtilityFunctions.transientSphereSeries( this.density, |
||||
this.thermalConduct, |
||||
this.heatConvection, |
||||
this.cp, |
||||
this.totalRadius, |
||||
ovenModel.tempInfini, |
||||
this.splitsNum, |
||||
this.deltar, |
||||
this.globTemp, |
||||
this.pointRadius |
||||
); |
||||
for (var i in this.totalLayers ){ |
||||
this.totalLayers[i].updateTemperatureTick(); |
||||
} |
||||
}; |
||||
|
||||
this.resetLayerTemps = function() { |
||||
for (var i in this.totalLayers ) { |
||||
this.totalLayers[i].resetLayerTemps(); |
||||
} |
||||
}; |
||||
|
||||
//Sheen Model Stuff
|
||||
this.globTemp=[]; |
||||
this.pointRadius = [] |
||||
this.splitsNum = 20; |
||||
this.deltar = this.totalRadius/this.splitsNum; //20 Data Points
|
||||
|
||||
this.initializePoints = function() { |
||||
var step = ( this.totalRadius - this.deltar ) / ( this.splitsNum - 1 ); |
||||
for (var i = 0; i<this.splitsNum ; i++ ) { |
||||
this.pointRadius.push(step*i+this.deltar); |
||||
this.globTemp.push(20+step*i); //Starts at 20 C for initilizating
|
||||
}
|
||||
}; |
||||
this.initializePoints() |
||||
|
||||
|
||||
} |
||||
|
||||
function OvenModel( turkeyWeight, gameState ) { |
||||
var that = this; |
||||
this.tempInfini=20; //C
|
||||
this.setTemp = 20; |
||||
this.globalTime = 0; |
||||
|
||||
var turkey = new TurkeyModel(turkeyWeight, this ); |
||||
var proportional = 0.004; // This value is arbitrary to how fast you want the temperatures to converge. (Or oscillate, which could be realistic as well)
|
||||
var errorTolerance = 10; //Stove is accurate to 1 degree Celcius Should hopefully oscillate below that value.
|
||||
// Equalize temp will need to be sent each time iteration
|
||||
this.equalizeTemp= function(){ |
||||
var error = Math.abs(this.setTemp-this.tempInfini); |
||||
if( this.setTemp>this.tempInfini ){ |
||||
this.tempInfini = this.tempInfini + error*proportional; |
||||
} |
||||
else if( this.setTemp<this.tempInfini ){ |
||||
this.tempInfini = this.tempInfini - error*proportional; |
||||
} |
||||
|
||||
if( error>errorTolerance ) { |
||||
return(true); |
||||
} |
||||
} |
||||
return { |
||||
getTurkeyState: function(){ |
||||
return { |
||||
"skin" : { |
||||
"temp": turkey.totalLayers[0].getTemperature(), |
||||
"cond": turkey.totalLayers[0].getCondition() |
||||
}, |
||||
"body" : { |
||||
"temp": turkey.totalLayers[1].getTemperature(), |
||||
"cond": turkey.totalLayers[1].getCondition() |
||||
}, |
||||
"core" : { |
||||
"temp": turkey.totalLayers[2].getTemperature(), |
||||
"cond": turkey.totalLayers[2].getCondition() |
||||
} |
||||
}; |
||||
}, |
||||
changeTemp: function(setTemp){ |
||||
if(DEBUG) console.log("temp changed to " + setTemp); |
||||
that.setTemp = setTemp; |
||||
}, |
||||
// set the tempInfini
|
||||
setRawTemp: function(newTemp){ |
||||
if(DEBUG) console.log("raw temp changed to" + that.tempInfini); |
||||
that.tempInfini = newTemp; |
||||
}, |
||||
getRawTemp: function(){ |
||||
return that.tempInfini; |
||||
}, |
||||
getCookTime: function(){ |
||||
return that.globalTime; |
||||
}, |
||||
secondTick: function(){ |
||||
that.globalTime = that.globalTime + 1; |
||||
if ( that.equalizeTemp() ) { |
||||
|
||||
// Turn on oven light
|
||||
gameState.pubsub.publish( "OvenLight", "On" ); |
||||
} |
||||
else { |
||||
// Turn off oven light
|
||||
gameState.pubsub.publish( "OvenLight", "Off" ); |
||||
} |
||||
if(DEBUG) console.log("Oven Temp " + that.tempInfini ) |
||||
turkey.updateLayerTemps(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
UtilityFunctions = { |
||||
|
||||
// Cache the lambda if the Biot number does not change, to avoid expensive root-finding operations
|
||||
cachedBiot: null, |
||||
cachedLambda: null, |
||||
|
||||
// Using Ratios for a rectangular Box Turkey
|
||||
calculateRadius: function(weight, density) { |
||||
|
||||
var ratioLvG=1.4; //1.4, Turkey length vs shoulder girth
|
||||
var ratioLvH=2; //2, Turkey length vs height from resting position
|
||||
|
||||
var length = Math.pow(weight/((1/ratioLvG)*(1/ratioLvH)*density),(1/3)) |
||||
var depth = 1/(ratioLvG /length); |
||||
var height = 1/(ratioLvH /length); |
||||
var simpleRadius = length/2; //Doesn't take into account equal Volume
|
||||
|
||||
var rectangleVolume = depth*height*length; //m^3 Multiple by 1/4 to account for triangular shape and empty Space
|
||||
var complexRadius = Math.pow(rectangleVolume/((4/3)*Math.PI), 1/3); //Volume of 3D Box = 3D Sphere
|
||||
|
||||
//if(DEBUG) console.log("Simple Radius " + simpleRadius + " Meters")
|
||||
//if(DEBUG) console.log("Complex Radius " + complexRadius + " Meters")
|
||||
return complexRadius; |
||||
}, |
||||
|
||||
sphereVolume: function(radius) { |
||||
return((4/3)*Math.PI*Math.pow(radius,3)) |
||||
}, |
||||
|
||||
waterLoss: function(temperature) { |
||||
return (Math.pow(10,(temperature-20)/80)-1) |
||||
}, |
||||
|
||||
transientSphereSeries: function( density, thermalConduct, heatConvection, cp, rTotal, tempInfinity, splitsNum, deltar, globTemp,pointRadius) { |
||||
|
||||
//Not Global Stuff
|
||||
var r0 = rTotal; |
||||
var deltat = 0.1 |
||||
|
||||
var alpha = thermalConduct/(density*cp) |
||||
var h=heatConvection;
|
||||
|
||||
for (var j=0; j<(1/deltat); j++ ) { |
||||
var dTdr=[] |
||||
// globTemp[splitsNum-1] should be last entry in globtemp
|
||||
for (var k=0; k<splitsNum; k++){ |
||||
if (k==0) { |
||||
dTdr.push((globTemp[1] - globTemp[0])/deltar) } |
||||
else if (k==splitsNum-1) { |
||||
dTdr.push((globTemp[splitsNum-1] - globTemp[splitsNum-2])/deltar)} |
||||
else { |
||||
dTdr.push((globTemp[k+1] - globTemp[k-1])/(2*deltar))} |
||||
} |
||||
dTdr[splitsNum-1] = heatConvection*(tempInfinity-globTemp[splitsNum-1])/thermalConduct |
||||
|
||||
var parenthesis = [] |
||||
for (var k=0; k<splitsNum; k++){ |
||||
parenthesis.push(dTdr[k]*Math.pow(pointRadius[k],2)) |
||||
} |
||||
|
||||
dPdr = [] |
||||
for (var k=0; k<splitsNum; k++){ |
||||
if (k==0) { |
||||
dPdr.push((parenthesis[1] - parenthesis[0])/deltar) } |
||||
else if (k==splitsNum-1) { |
||||
dPdr.push((parenthesis[splitsNum-1] - parenthesis[splitsNum-2])/deltar)} |
||||
else { |
||||
dPdr.push((parenthesis[k+1] - parenthesis[k-1])/(2*deltar))} |
||||
} |
||||
|
||||
for (var k=0; k<splitsNum; k++){ |
||||
globTemp[k]=alpha*dPdr[k]/Math.pow(pointRadius[k],2)*deltat + globTemp[k] //dTdr * deltaT in one loop
|
||||
} |
||||
//dTdt(1)=dTdt(1)/2;
|
||||
} |
||||
console.log("dPdr" + dPdr) |
||||
return(globTemp) |
||||
}, |
||||
|
||||
/* Utility Functions */ |
||||
C2F: function( celsius ){ |
||||
return ( (celsius*(9/5)) + 32 ); |
||||
}, |
||||
F2C: function( farenheit ) { |
||||
return ( (farenheit-32) *(5/9) ); |
||||
}, |
||||
lbs2kgs: function(pounds){ |
||||
return pounds * 0.453592 |
||||
}, |
||||
randRange: function(min, max){ |
||||
return Math.floor(Math.random()*(max-min+1))+min; |
||||
}, |
||||
cookCondition: function(cookValue,volume){ |
||||
var multiplier = 1; |
||||
if (cookValue>=multiplier*600000) { |
||||
return ["Fire", (cookValue-600000)/(multiplier*600000),"fire"]; |
||||
} |
||||
else if(cookValue>=multiplier*250000) { |
||||
return ["Burnt", (cookValue-250000)/(multiplier*600000), "burnt"]; |
||||
} |
||||
else if (cookValue>=multiplier*150000) { |
||||
return ["Dry", (cookValue-150000)/(multiplier*250000), "dry"]; |
||||
} |
||||
else if (cookValue>=multiplier*85000){ |
||||
return ["Cooked", (cookValue-12000)/(multiplier*150000), "overcooked"]; |
||||
} |
||||
else if (cookValue>=multiplier*12000) { |
||||
return ["Cooked", (cookValue-12000)/(multiplier*150000), "cooked"]; |
||||
} |
||||
else if (cookValue>=multiplier*10000){ |
||||
return ["Undercooked", (cookValue-5000)/(multiplier*12000), "slightly cooked"]; |
||||
} |
||||
else if (cookValue>=multiplier*5000) { |
||||
return ["Undercooked", (cookValue-5000)/(multiplier*12000), "undercooked"]; |
||||
} |
||||
else { |
||||
return ["Raw", 1, "raw"]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
//Running the Program Stuff
|
||||
/* |
||||
var ovenObject = new OvenModel(); |
||||
var turkey = new TurkeyModel(9, ovenObject); |
||||
|
||||
globalTime=0; |
||||
setInterval(function(){ovenObject.secondTick();},1000); |
||||
*/ |
Loading…
Reference in new issue