@@ -1775,58 +1775,100 @@ class OrbitalSentinelApp {
17751775
17761776 private updateTorinoScale (
17771777 probability : number ,
1778- energy : number ,
1778+ energyJoules : number ,
17791779 distanceAU : number ,
1780- radiusKm : number
1780+ radiusMeters : number
17811781 ) : void {
1782- // Enhanced Torino scale calculation
1783- // Based on impact probability, kinetic energy, proximity, and size
1782+ // Scientifically accurate Torino Scale calculation
1783+ // Based on: https://cneos.jpl.nasa.gov/sentry/torino_scale.html
1784+
1785+ // Convert kinetic energy from Joules to Megatons TNT
1786+ // 1 Megaton = 4.184e15 Joules
1787+ const energyMT = energyJoules / 4.184e15 ;
1788+
1789+ // Energy-based threat thresholds (in Megatons TNT)
1790+ const isHarmless = energyMT < 0.001 ; // < 1 kiloton - burns up
1791+ const isLocal = energyMT >= 0.001 && energyMT < 1 ; // 1kt-1MT - local damage
1792+ const isRegional = energyMT >= 1 && energyMT < 100 ; // 1-100 MT - regional
1793+ const isNational = energyMT >= 100 && energyMT < 1000 ; // 100-1000 MT - national
1794+ const isGlobal = energyMT >= 1000 && energyMT < 100000 ; // 1-100 Gigatons
1795+ const isExtinction = energyMT >= 100000 ; // > 100 Gigatons
1796+
1797+ // MOID-based hazard assessment
1798+ const isPHA = distanceAU < 0.05 ; // Potentially Hazardous Asteroid threshold
1799+
17841800 let level = 0 ;
17851801
1786- // Distance-based assessment (closer = higher level)
1787- const isClose = distanceAU < 0.05 ; // < 0.05 AU is concerning
1788- const isVeryClose = distanceAU < 0.01 ; // < 0.01 AU is very concerning
1789-
1790- // Size-based assessment
1791- const isLarge = radiusKm > 0.05 ; // > 50m can cause local damage
1792- const isVeryLarge = radiusKm > 0.5 ; // > 500m can cause regional damage
1793- const isMassive = radiusKm > 1.0 ; // > 1km can cause global effects
1794-
1795- // Base level from probability
1796- if ( probability < 1e-6 && ! isVeryClose ) {
1797- level = 0 ; // No hazard
1798- } else if ( probability < 1e-4 && ! isClose ) {
1799- level = 1 ; // Normal
1800- } else if ( isVeryClose && isMassive ) {
1801- // Very close + massive = high threat
1802- level = Math . min ( 10 , 7 + Math . floor ( probability * 6 ) ) ;
1803- } else if ( isVeryClose && isVeryLarge ) {
1804- level = Math . min ( 8 , 5 + Math . floor ( probability * 6 ) ) ;
1805- } else if ( isClose && isLarge ) {
1806- level = Math . min ( 6 , 3 + Math . floor ( Math . log10 ( energy + 1 ) / 4 ) ) ;
1802+ // Torino Scale Logic (simplified from official scale)
1803+ // Level 0: No hazard - collision extremely unlikely
1804+ // Level 1: Normal - routine discovery, very unlikely collision
1805+ // Levels 2-4: Meriting attention by astronomers
1806+ // Levels 5-7: Threatening - governmental attention warranted
1807+ // Levels 8-10: Certain/near-certain collision
1808+
1809+ if ( probability < 1e-7 || isHarmless ) {
1810+ // No real hazard - energy too low or probability negligible
1811+ level = 0 ;
1812+ } else if ( probability < 1e-6 ) {
1813+ // Very low probability
1814+ level = isPHA ? 1 : 0 ;
1815+ } else if ( probability < 1e-4 ) {
1816+ // Low probability, merit attention
1817+ if ( isExtinction ) level = 4 ;
1818+ else if ( isGlobal ) level = 3 ;
1819+ else if ( isNational ) level = 2 ;
1820+ else level = 1 ;
18071821 } else if ( probability < 1e-2 ) {
1808- level = Math . min ( 4 , Math . floor ( 2 + Math . log10 ( energy + 1 ) / 5 ) ) ;
1822+ // Moderate probability
1823+ if ( isExtinction ) level = 6 ;
1824+ else if ( isGlobal ) level = 5 ;
1825+ else if ( isNational ) level = 4 ;
1826+ else if ( isRegional ) level = 3 ;
1827+ else level = 2 ;
18091828 } else if ( probability < 0.5 ) {
1810- level = Math . min ( 7 , Math . floor ( 4 + Math . log10 ( energy + 1 ) / 5 ) ) ;
1829+ // Significant probability
1830+ if ( isExtinction ) level = 8 ;
1831+ else if ( isGlobal ) level = 7 ;
1832+ else if ( isNational ) level = 6 ;
1833+ else if ( isRegional ) level = 5 ;
1834+ else level = 4 ;
1835+ } else if ( probability < 0.99 ) {
1836+ // High probability
1837+ if ( isExtinction ) level = 9 ;
1838+ else if ( isGlobal ) level = 8 ;
1839+ else if ( isNational ) level = 7 ;
1840+ else if ( isRegional ) level = 6 ;
1841+ else level = 5 ;
18111842 } else {
1812- level = Math . min ( 10 , Math . floor ( 7 + probability * 6 ) ) ;
1843+ // Certain collision (>99%)
1844+ if ( isExtinction ) level = 10 ;
1845+ else if ( isGlobal ) level = 10 ;
1846+ else if ( isNational ) level = 9 ;
1847+ else if ( isRegional ) level = 8 ;
1848+ else if ( isLocal ) level = 8 ;
1849+ else level = 0 ; // Too small to matter even with certain collision
18131850 }
18141851
1815- // Ensure level is valid
1852+ // Ensure level is valid 0-10
18161853 level = Math . max ( 0 , Math . min ( 10 , Math . floor ( level ) ) ) ;
18171854
1855+ // Format energy and size for display
1856+ const energyStr = energyMT >= 1000 ? `${ ( energyMT / 1000 ) . toFixed ( 0 ) } GT` : `${ energyMT . toFixed ( 2 ) } MT` ;
1857+ const diameterM = radiusMeters * 2 ;
1858+ const sizeStr = diameterM >= 1000 ? `${ ( diameterM / 1000 ) . toFixed ( 1 ) } km` : `${ diameterM . toFixed ( 0 ) } m` ;
1859+
18181860 const torinoDescriptions : Record < number , string > = {
1819- 0 : ' No hazard - Likelihood of collision is zero' ,
1820- 1 : ' Normal - A routine discovery with no unusual concern' ,
1821- 2 : ' Meriting attention - A somewhat close but not unusual encounter' ,
1822- 3 : ' Meriting attention - Close encounter deserving attention' ,
1823- 4 : ' Meriting attention - Close encounter with 1%+ chance of collision' ,
1824- 5 : ' Threatening - Close encounter posing a serious threat' ,
1825- 6 : ' Threatening - Close encounter with large object, significant threat' ,
1826- 7 : ' Threatening - Extremely close encounter with large object' ,
1827- 8 : ' Certain collision - Localized destruction expected' ,
1828- 9 : ' Certain collision - Regional devastation expected' ,
1829- 10 : ' Certain collision - Global climatic catastrophe'
1861+ 0 : ` No hazard - ${ isHarmless ? 'Energy too low (<1 kt)' : 'Collision impossible' } ` ,
1862+ 1 : ` Normal - ${ sizeStr } object ( ${ energyStr } )` ,
1863+ 2 : ` Meriting attention - ${ sizeStr } close pass ( ${ energyStr } )` ,
1864+ 3 : ` Meriting attention - ${ sizeStr } regional threat ( ${ energyStr } )` ,
1865+ 4 : ` Meriting attention - ${ sizeStr } 1%+ collision ( ${ energyStr } )` ,
1866+ 5 : ` Threatening - ${ sizeStr } regional destruction ( ${ energyStr } )` ,
1867+ 6 : ` Threatening - ${ sizeStr } national threat ( ${ energyStr } )` ,
1868+ 7 : ` Threatening - ${ sizeStr } global effects ( ${ energyStr } )` ,
1869+ 8 : ` Certain impact - ${ sizeStr } local destruction ( ${ energyStr } )` ,
1870+ 9 : ` Certain impact - ${ sizeStr } continental ( ${ energyStr } )` ,
1871+ 10 : ` Certain impact - ${ sizeStr } global catastrophe ( ${ energyStr } )`
18301872 } ;
18311873
18321874 const torinoDisplay = document . getElementById ( 'torino-display' ) ;
@@ -1838,7 +1880,7 @@ class OrbitalSentinelApp {
18381880 torinoValue . textContent = String ( level ) ;
18391881 torinoDescription . textContent = torinoDescriptions [ level ] || 'Unknown' ;
18401882
1841- // Highlight active segment
1883+ // Highlight active segment based on Torino color coding
18421884 document . querySelectorAll ( '.torino-segment' ) . forEach ( seg => seg . classList . remove ( 'active' ) ) ;
18431885 if ( level === 0 ) {
18441886 document . querySelector ( '.torino-segment.white' ) ?. classList . add ( 'active' ) ;
0 commit comments