tools(rpo-replay): v4 WIP — data-driven model from public element sets, true time-lapse engine with motion blur, timeline, label hysteresis

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0187rSCa4AKJPGw6uiuPSGyX
This commit is contained in:
Claude committed 2026-11-08 05:21:12 +00:00
1 parent 1a06c3e4cf
commit fff5d5a479
18 files changed
+2495 -375

No files matched your search

+1 -1
View File
@@ -1 +1 @@
{"t0":"2026-09-29T12:00:00Z","dt":60,"a_km":6920.86656753336,"x36":[-3625.5189,-5318.3928,2553.1804,-3766.6429,-5406.7449,2129.5637,-3891.4855,-5471.7261,1696.7165,-3999.5064,-5513.0548,1256.5148,-4090.2389,-5530.5527,810.8666,-4163.2919,-5524.1455,361.704,-4218.3513,-5493.863,-89.0263,-4255.1815,-5439.8394,-539.3709,-4273.6267,-5362.3124,-987.3787,-4273.6112,-5261.6219,-1431.1097,-4255.1399,-5138.2088,-1868.6435,-4218.2979,-4992.6125,-2298.0876,-4163.2501,-4825.4685,-2717.5863,-4090.2405,-4637.5056,-3125.3281,-3999.5906,-4429.5422,-3519.5541,-3891.6983,-4202.4827,-3898.5655,-3767.0355,-3957.3135,-4260.7306,-3626.1461,-3695.098,-4604.4922,-3469.6435,-3416.9724,-4928.3736,-3298.2077,-3124.1403,-5230.9851,-3112.5823,-2817.8673,-5511.0297,-2913.5708,-2499.4756,-5767.3082,-2702.0336,-2170.3385,-5998.7243,-2478.884,-1831.874,-6204.2888,-2245.084,-1485.5388,-6383.1236,-2001.6406,-1132.8228,-6534.465,-1749.6012,-775.2418,-6657.667,-1490.0494,-414.3321,-6752.2034,-1224.1004,-51.6435,-6817.6704,-952.8961,311.2668,-6853.7876,-677.601,672.8408,-6860.3993,-399.3967,1031.526,-6837.4753,-119.4774,1385.782,-6785.1109,160.9549,1734.0865,-6703.5265,440.6958,2074.9421,-6593.067,718.5431,2406.882,-6454.2004,993.3019,2728.4768,-6287.5159,1263.7898,3038.3397,-6093.7221,1528.8414,3335.1331,-5873.6438,1787.3137,3617.5742,-5628.2193,2038.0909,3884.4401,-5358.4963,2280.089,4134.5737,-5065.628,2512.2606,4366.8886,-4750.8684,2733.5996,4580.374,-4415.5673,2943.1456,4774.0993,-4061.1646,3139.9881,4947.2182,-3689.1846,3323.2708,5098.9729,-3301.2294,3492.1953,5228.6976,-2898.9722,3646.0251,5335.8216,-2484.1499,3784.0885,5419.8722,-2058.5561,3905.7822,5480.4768,-1624.0326,4010.5739,5517.3649,-1182.4621,4098.0049,5530.3699,-735.7589,4167.6921,5519.4291,-285.8613,4219.3301,5484.5851,165.2776,4252.6923,5425.985,615.6986,4267.6325,5343.8803,1063.4451,4264.0848,5238.6252,1506.572,4242.0646,5110.6759,1943.1542,4201.6682,4960.5876,2371.2956,4143.0722,4789.0125,2789.1375,4066.533,4596.6966,3194.8666,3972.3851,4384.4763,3586.7233,3861.0399,4153.2748,3963.0095,3732.9835,3904.0971,4322.0959,3588.7745,3638.0265,4662.4289,3429.0413,3356.2186,4982.5378,3254.4793,3059.8969,5281.0405,3065.8479,2750.347,5556.6496,2863.9667,2428.9108,5808.1779,2649.7119,2096.9807,6034.543,2424.0126,1755.9934,6234.7722,2187.8466,1407.424,6408.0057,1942.2361,1052.779,6553.5008,1688.2435,693.5905,6670.6345,1426.9663,331.4093,6758.906,1159.5332,-32.2016,6817.9385,887.0984,-395.6736,6847.4811,610.8377,-757.4391,6847.4092,331.9425,-1115.938,6817.7252,51.6159,-1469.6244,6758.5586,-228.9334,-1816.9732,6670.1653,-508.4954,-2156.4859,6552.9263,-785.8643,-2486.6979,6407.3469,-1059.8435,-2806.1839,6234.0537,-1329.2506,-3113.5641,6033.7929,-1592.9226,-3407.5106,5807.4269,-1849.7206,-3686.7526,5555.9306,-2098.535,-3950.0821,5280.3881,-2338.2901,-4196.3592,4981.9873,-2567.9488,-4424.5171,4662.0157,-2786.5173,-4633.5668,4321.8548,-2993.0494,-4822.6018,3962.9741,-3186.6507,-4990.8017,3586.925,-3366.4824,-5137.4364,3195.3341,-3531.7656,-5261.8692,2789.8963,-3681.7841,-5363.5598,2372.3671,-3815.8884,-5442.0668,1944.5552,-3933.498,-5497.0496,1508.3147,-4034.1043,-5528.2705,1065.5362,-4117.273,-5535.5952,618.1391,-4182.6458,-5518.9936,168.063,-4229.9422,-5478.5403,-282.7416,-4258.9606,-5414.4137,-732.321,-4269.5793,-5326.8954,-1178.7281,-4261.7566,-5216.369,-1620.03,-4235.5314,-5083.318,-2054.3173,-4191.0225,-4928.3236,-2479.7125,-4128.4281,-4752.0621,-2894.3776,-4048.0249,-4555.3015,-3296.5229,-3950.1663,-4338.8981,-3684.4144,-3835.2812,-4103.7925,-4056.3812,-3703.8716,-3851.0055,-4410.8229,-3556.5102,-3581.633,-4746.216,-3393.8381,-3296.8416,-5061.1204,-3216.5615,-2997.8631,-5354.1857,-3025.4486,-2685.989,-5624.1563,-2821.3264,-2362.5652,-5869.8766,-2605.0768,-2028.9858,-6090.2958,-2377.6327,-1686.6876,-6284.472,-2139.9745,-1337.1435,-6451.5758,-1893.1251,-981.8563,-6590.8939,-1638.1463,-622.3528,-6701.8315,-1376.134,-260.1771,-6783.9148,-1108.2137,103.1156,-6836.7927,-835.5356,465.9655,-6860.2385,-559.2705,826.815,-6854.1503,-280.6041,1184.1145,-6818.552,-0.7327,1536.3291,-6753.5926,279.1418,1881.9451,-6659.5462,557.817,2219.4761,-6536.8107,834.095,2547.4691,-6385.9067,1106.7874,2864.5109,-6207.4749,1374.7199,3169.2339,-6002.2742,1636.738,3460.3218,-5771.1787,1891.7111,3736.5156,-5515.1741,2138.5378,3996.6187,-5235.3541,2376.1508,4239.5027,-4932.9158,2603.521,4464.1117,-4609.155,2819.6626,4669.4677,-4265.461,3023.6373,4854.6748,-3903.3108,3214.5582,5018.9232,-3524.2629,3391.5945,5161.4933,-3129.9508,3553.9745,5281.7589,-2722.076,3700.9899,5379.1903,-2302.4007,3831.9985,5453.3567,-1872.7403,3946.4277,5503.9286,-1434.9551,4043.7766,5530.6795,-990.9423,4123.6191,5533.4868,-542.6278,4185.6053,5512.3327,-91.9572,4229.4634,5467.3044,359.1127,4255.0011,5398.5938,808.6228,4262.1061,5306.4965,1254.6202,4250.7472,5191.4108,1695.1675,4220.9739,5053.8358,2128.3511,4172.9164,4894.3688,2552.2897,4106.785,4713.7032,2965.1433,4022.8689,4512.6245,3365.1207,3921.5348,4292.0072,3750.4879,3803.2253,4052.811,4Line truncated
{"t0":"2026-09-29T12:00:00Z","dt":60,"a_km":6921.372691085778,"a_ref_km":6920.0,"x36":[-3636.5139,-5325.6638,2523.2654,-3776.5096,-5412.4061,2098.9707,-3900.1841,-5475.7572,1665.5797,-4007.0029,-5515.4434,1224.9706,-4096.5051,-5531.2942,779.0531,-4168.3055,-5523.2431,329.7599,-4222.0959,-5491.3278,-120.9621,-4257.647,-5435.6901,-571.1601,-4274.809,-5356.5752,-1018.8846,-4273.5121,-5254.3303,-1462.1974,-4253.7672,-5129.4034,-1899.1809,-4215.6653,-4982.3406,-2327.946,-4159.3769,-4813.7838,-2746.6409,-4085.1516,-4624.4681,-3153.4587,-3993.3163,-4415.2174,-3546.6454,-3884.2738,-4186.9416,-3924.5078,-3758.5011,-3940.6318,-4285.4201,-3616.5466,-3677.3561,-4627.8314,-3459.0281,-3398.2545,-4950.2719,-3286.6295,-3104.5344,-5251.3587,-3100.0982,-2797.4644,-5529.8021,-2900.2412,-2478.3695,-5784.4104,-2687.922,-2148.6252,-6014.0949,-2464.0566,-1809.6512,-6217.8741,-2229.6096,-1462.9058,-6394.8777,-1985.5901,-1109.8796,-6544.3502,-1733.0473,-752.0892,-6665.6532,-1473.0664,-391.0708,-6758.2687,-1206.7638,-28.374,-6821.8005,-935.2824,334.4444,-6855.9762,-659.7873,695.8277,-6860.6479,-381.4604,1054.2244,-6835.7929,-101.4961,1408.0959,-6781.5141,178.9039,1755.9218,-6698.0393,458.5355,2096.2069,-6585.7205,736.1974,2427.4872,-6445.0327,1010.6955,2748.3357,-6276.5723,1280.8486,3057.369,-6081.0542,1545.4928,3353.2529,-5859.3101,1803.4868,3634.7078,-5612.2842,2053.7166,3900.515,-5341.0304,2295.1003,4149.5213,-5046.7077,2526.5929,4380.6447,-4730.5756,2747.1908,4592.8788,-4393.9893,2955.9365,4785.2977,-4038.3938,3151.9223,4957.06,-3665.3179,3334.2952,5107.4132,-3276.3684,3502.2602,5235.6966,-2873.2223,3655.0842,5341.345,-2457.6207,3792.0994,5423.8912,-2031.3603,3912.7064,5482.9686,-1596.2862,4016.377,5518.3126,-1154.2836,4102.6567,5529.7625,-707.2693,4171.1671,5517.2618,-257.1833,4221.6071,5480.8593,194.0199,4253.7552,5420.7085,644.38,4267.4696,5337.0671,1091.94,4262.6897,5230.2959,1534.7546,4239.4358,5100.8572,1970.8995,4197.8093,4949.3128,2398.4794,4137.992,4776.321,2815.6372,4060.2455,4582.6341,3220.5616,3964.9095,4369.0948,3611.4956,3852.4006,4136.6318,3986.7443,3723.21,3886.2564,4344.682,3577.9013,3619.0572,4683.7593,3417.1081,3336.1953,5002.5102,3241.5307,3038.8993,5299.5578,3051.9331,2728.4595,5573.6206,2849.1395,2406.2223,5823.5174,2634.0305,2073.5843,6048.1727,2407.5394,1731.9861,6246.6206,2170.648,1382.9058,6418.0091,1924.3821,1027.8532,6561.6033,1669.8075,668.3627,6676.7885,1408.0247,305.9871,6763.0725,1140.1652,-57.7091,6820.0873,867.3856,-421.1563,6847.5911,590.8636,-782.7863,6845.4685,311.7924,-1141.0393,6813.7315,31.3762,-1494.3698,6752.519,-249.1755,-1841.2539,6662.0964,-528.6522,-2180.1949,6542.8545,-805.8484,-2509.7303,6395.3079,-1079.5677,-2828.4376,6220.0927,-1348.6289,-3134.9406,6017.9643,-1611.8702,-3427.9149,5789.7941,-1868.1546,-3706.0943,5536.5659,-2116.3747,-3968.2755,5259.372,-2355.4576,-4213.324,4959.4084,-2584.3693,-4440.1788,4637.9703,-2802.1194,-4647.857,4296.4462,-3007.7657,-4835.4587,3936.3122,-3200.4179,-5002.1703,3559.1258,-3379.2419,-5147.2689,3166.519,-3543.4636,-5270.1251,2760.1914,-3692.3721,-5370.2064,2341.9027,-3825.3231,-5447.079,1913.465,-3941.7417,-5500.4104,1476.735,-4041.1253,-5529.9706,1033.6053,-4123.0454,-5535.6333,585.9966,-4187.1497,-5517.3764,135.8488,-4233.1638,-5475.2822,-314.8875,-4260.8921,-5409.5368,-764.26,-4270.2191,-5320.4293,-1210.3229,-4261.1091,-5208.3505,-1651.1459,-4233.607,-5073.7909,-2084.8225,-4187.8373,-4917.3385,-2509.4788,-4124.004,-4739.6759,-2923.281,-4042.3891,-4541.577,-3324.4444,-3943.3515,-4323.9038,-3711.24,-3827.325,-4087.6022,-4082.0028,-3694.8164,-3833.6975,-4435.1385,-3546.403,-3563.2902,-4769.1302,-3382.7301,-3277.5506,-5082.5447,-3204.5078,-2977.714,-5374.0386,-3012.508,-2665.0749,-5642.3639,-2807.561,-2340.9818,-5886.3724,-2590.5517,-2006.8309,-6105.0213,-2362.4157,-1664.0604,-6297.3763,-2124.1357,-1314.1445,-6462.6162,-1876.7367,-958.5869,-6600.0355,-1621.2823,-598.9148,-6709.0474,-1358.8697,-236.6723,-6789.1862,-1090.6255,126.586,-6840.1086,-817.701,489.301,-6861.5959,-541.2671,849.9161,-6853.5539,-262.5101,1206.8831,-6816.0141,17.3739,1558.6692,-6749.1331,297.1832,1903.7625,-6653.1924,575.716,2240.6791,-6528.5971,851.7752,2567.9687,-6375.8748,1124.1735,2884.221,-6195.6731,1391.7379,3188.0716,-5988.7576,1653.3154,3478.2079,-5756.0088,1907.7771,3753.3743,-5498.4187,2154.0239,4012.3785,-5217.087,2390.9903,4254.0961,-4913.2165,2617.65,4477.4757,-4588.1085,2833.0198,4681.544,-4243.1576,3036.1641,4865.4097,-3879.8457,3226.1992,5028.2684,-3499.736,3402.2976,5169.4055,-3104.4664,3563.6911,5288.2002,-2695.7423,3709.675,5384.1285,-2275.3296,3839.6111,5456.7654,-1845.0471,3952.9306,5505.7873,-1406.7579,4049.1372,5530.9736,-962.362,4127.8089,5532.2079,-513.7871,4188.6005,5509.4787,-62.9806,4231.2448,5462.8794,388.0994,4255.5543,5392.6084,837.493,4261.4217,5298.9679,1283.2473,4248.8209,5182.3623,1723.4247,4217.8063,5043.2974,2156.1126,4168.5135,4882.3768,2579.431,4101.1579,4700.3001,2991.5414,4016.0339,4497.8592,3390.6552,3913.5135,4275.9347,3775.0411,3794Line truncated
+1
View File
@@ -0,0 +1 @@
{"t0":"2026-09-29T12:00:00Z","dt":60,"a_km":6920.86656753336,"x36":[-3625.5189,-5318.3928,2553.1804,-3766.6429,-5406.7449,2129.5637,-3891.4855,-5471.7261,1696.7165,-3999.5064,-5513.0548,1256.5148,-4090.2389,-5530.5527,810.8666,-4163.2919,-5524.1455,361.704,-4218.3513,-5493.863,-89.0263,-4255.1815,-5439.8394,-539.3709,-4273.6267,-5362.3124,-987.3787,-4273.6112,-5261.6219,-1431.1097,-4255.1399,-5138.2088,-1868.6435,-4218.2979,-4992.6125,-2298.0876,-4163.2501,-4825.4685,-2717.5863,-4090.2405,-4637.5056,-3125.3281,-3999.5906,-4429.5422,-3519.5541,-3891.6983,-4202.4827,-3898.5655,-3767.0355,-3957.3135,-4260.7306,-3626.1461,-3695.098,-4604.4922,-3469.6435,-3416.9724,-4928.3736,-3298.2077,-3124.1403,-5230.9851,-3112.5823,-2817.8673,-5511.0297,-2913.5708,-2499.4756,-5767.3082,-2702.0336,-2170.3385,-5998.7243,-2478.884,-1831.874,-6204.2888,-2245.084,-1485.5388,-6383.1236,-2001.6406,-1132.8228,-6534.465,-1749.6012,-775.2418,-6657.667,-1490.0494,-414.3321,-6752.2034,-1224.1004,-51.6435,-6817.6704,-952.8961,311.2668,-6853.7876,-677.601,672.8408,-6860.3993,-399.3967,1031.526,-6837.4753,-119.4774,1385.782,-6785.1109,160.9549,1734.0865,-6703.5265,440.6958,2074.9421,-6593.067,718.5431,2406.882,-6454.2004,993.3019,2728.4768,-6287.5159,1263.7898,3038.3397,-6093.7221,1528.8414,3335.1331,-5873.6438,1787.3137,3617.5742,-5628.2193,2038.0909,3884.4401,-5358.4963,2280.089,4134.5737,-5065.628,2512.2606,4366.8886,-4750.8684,2733.5996,4580.374,-4415.5673,2943.1456,4774.0993,-4061.1646,3139.9881,4947.2182,-3689.1846,3323.2708,5098.9729,-3301.2294,3492.1953,5228.6976,-2898.9722,3646.0251,5335.8216,-2484.1499,3784.0885,5419.8722,-2058.5561,3905.7822,5480.4768,-1624.0326,4010.5739,5517.3649,-1182.4621,4098.0049,5530.3699,-735.7589,4167.6921,5519.4291,-285.8613,4219.3301,5484.5851,165.2776,4252.6923,5425.985,615.6986,4267.6325,5343.8803,1063.4451,4264.0848,5238.6252,1506.572,4242.0646,5110.6759,1943.1542,4201.6682,4960.5876,2371.2956,4143.0722,4789.0125,2789.1375,4066.533,4596.6966,3194.8666,3972.3851,4384.4763,3586.7233,3861.0399,4153.2748,3963.0095,3732.9835,3904.0971,4322.0959,3588.7745,3638.0265,4662.4289,3429.0413,3356.2186,4982.5378,3254.4793,3059.8969,5281.0405,3065.8479,2750.347,5556.6496,2863.9667,2428.9108,5808.1779,2649.7119,2096.9807,6034.543,2424.0126,1755.9934,6234.7722,2187.8466,1407.424,6408.0057,1942.2361,1052.779,6553.5008,1688.2435,693.5905,6670.6345,1426.9663,331.4093,6758.906,1159.5332,-32.2016,6817.9385,887.0984,-395.6736,6847.4811,610.8377,-757.4391,6847.4092,331.9425,-1115.938,6817.7252,51.6159,-1469.6244,6758.5586,-228.9334,-1816.9732,6670.1653,-508.4954,-2156.4859,6552.9263,-785.8643,-2486.6979,6407.3469,-1059.8435,-2806.1839,6234.0537,-1329.2506,-3113.5641,6033.7929,-1592.9226,-3407.5106,5807.4269,-1849.7206,-3686.7526,5555.9306,-2098.535,-3950.0821,5280.3881,-2338.2901,-4196.3592,4981.9873,-2567.9488,-4424.5171,4662.0157,-2786.5173,-4633.5668,4321.8548,-2993.0494,-4822.6018,3962.9741,-3186.6507,-4990.8017,3586.925,-3366.4824,-5137.4364,3195.3341,-3531.7656,-5261.8692,2789.8963,-3681.7841,-5363.5598,2372.3671,-3815.8884,-5442.0668,1944.5552,-3933.498,-5497.0496,1508.3147,-4034.1043,-5528.2705,1065.5362,-4117.273,-5535.5952,618.1391,-4182.6458,-5518.9936,168.063,-4229.9422,-5478.5403,-282.7416,-4258.9606,-5414.4137,-732.321,-4269.5793,-5326.8954,-1178.7281,-4261.7566,-5216.369,-1620.03,-4235.5314,-5083.318,-2054.3173,-4191.0225,-4928.3236,-2479.7125,-4128.4281,-4752.0621,-2894.3776,-4048.0249,-4555.3015,-3296.5229,-3950.1663,-4338.8981,-3684.4144,-3835.2812,-4103.7925,-4056.3812,-3703.8716,-3851.0055,-4410.8229,-3556.5102,-3581.633,-4746.216,-3393.8381,-3296.8416,-5061.1204,-3216.5615,-2997.8631,-5354.1857,-3025.4486,-2685.989,-5624.1563,-2821.3264,-2362.5652,-5869.8766,-2605.0768,-2028.9858,-6090.2958,-2377.6327,-1686.6876,-6284.472,-2139.9745,-1337.1435,-6451.5758,-1893.1251,-981.8563,-6590.8939,-1638.1463,-622.3528,-6701.8315,-1376.134,-260.1771,-6783.9148,-1108.2137,103.1156,-6836.7927,-835.5356,465.9655,-6860.2385,-559.2705,826.815,-6854.1503,-280.6041,1184.1145,-6818.552,-0.7327,1536.3291,-6753.5926,279.1418,1881.9451,-6659.5462,557.817,2219.4761,-6536.8107,834.095,2547.4691,-6385.9067,1106.7874,2864.5109,-6207.4749,1374.7199,3169.2339,-6002.2742,1636.738,3460.3218,-5771.1787,1891.7111,3736.5156,-5515.1741,2138.5378,3996.6187,-5235.3541,2376.1508,4239.5027,-4932.9158,2603.521,4464.1117,-4609.155,2819.6626,4669.4677,-4265.461,3023.6373,4854.6748,-3903.3108,3214.5582,5018.9232,-3524.2629,3391.5945,5161.4933,-3129.9508,3553.9745,5281.7589,-2722.076,3700.9899,5379.1903,-2302.4007,3831.9985,5453.3567,-1872.7403,3946.4277,5503.9286,-1434.9551,4043.7766,5530.6795,-990.9423,4123.6191,5533.4868,-542.6278,4185.6053,5512.3327,-91.9572,4229.4634,5467.3044,359.1127,4255.0011,5398.5938,808.6228,4262.1061,5306.4965,1254.6202,4250.7472,5191.4108,1695.1675,4220.9739,5053.8358,2128.3511,4172.9164,4894.3688,2552.2897,4106.785,4713.7032,2965.1433,4022.8689,4512.6245,3365.1207,3921.5348,4292.0072,3750.4879,3803.2253,4052.811,4Line truncated
+196 -190
View File
@@ -1,72 +1,62 @@
// HUD for the RPO replay, sized for phone playback: a 16:9 video in a feed
// plays ~390 px wide (≈0.2×), so body copy is ≥34 px at 1080p and nothing
// that must be read is under ~24 px. Every animation is a pure function of
// the video time `s`, so frames render deterministically.
// HUD for the RPO replay (v4), sized for phone playback: a 16:9 video in a
// feed plays ~390 px wide (≈0.2×), so body copy is ≥34 px at 1080p and nothing
// that must be read is under ~24 px. Animation is a function of video time `s`,
// except label placement, which carries state between consecutive frames so
// labels hold their side (hysteresis) and ease instead of jumping.
export function createHud(ctx) {
const { D, W, H, U, PORTRAIT, sec, rate, sOfTau, sepAt, CA_T, VANTOR_T, BURNS, CUTS, project } = ctx;
const { D, W, H, U, PORTRAIT, sec, rate, sOfTau, CA_T, VANTOR_T, BURNS, CUTS, SHOTS, shotAt, DUR, project } = ctx;
const $ = (q) => document.querySelector(q);
const CA_M = Math.round(D.ca.m / 5) * 5;
const END = SHOTS.find((x) => x.id === 'end').s0;
// -------------------------------------------------------------------------
// Story
// -------------------------------------------------------------------------
const span = (id) => {
const x = SHOTS.find((sh) => sh.id === id);
return [x.s0, x.s1];
};
const CHAPTERS = [
{ key: 'climb', label: 'Climb', s0: 4.4, s1: 12.5 },
{ key: 'drop', label: 'Drop in', s0: 12.5, s1: 22.0 },
{ key: 'close', label: 'Close in', s0: 22.0, s1: 29.7 },
{ key: 'closest', label: 'Closest', s0: 29.7, s1: 35.6 },
{ key: 'leave', label: 'Leave', s0: 35.6, s1: 41.2 },
{ key: 'climb', label: 'Climb', s0: span('climb')[0], s1: span('lap')[1] },
{ key: 'drop', label: 'Drop in', s0: span('drop')[0], s1: span('close')[1] },
{ key: 'dance', label: 'Close in', s0: span('dance')[0], s1: span('approach')[1] },
{ key: 'closest', label: 'Closest', s0: span('ca')[0], s1: span('sunrise')[1] },
{ key: 'leave', label: 'Leave', s0: span('leave')[0], s1: span('leave')[1] },
];
const cap = (id, title, body, extra = {}) => ({ s0: span(id)[0], s1: span(id)[1], title, body, ...extra });
const CAPTIONS = [
{
s0: -1, s1: 4.4, hook: true,
title: `A Russian military satellite shadowed a U.S.-registered one — to <span class="c-cos" style="white-space:nowrap">~${CA_M} m</span>`,
body: 'How <b class="c-cos">Cosmos 2612</b> matched <b class="c-x36">ICEYE-X36</b>\'s orbit over 22 days, rebuilt from public tracking data.',
},
{
s0: 4.4, s1: 12.5,
title: 'Climb higher to fall behind',
body: 'A higher orbit is slower. Lifted <b>47 km</b>, it drifts back and laps its target.',
},
{
s0: 12.5, s1: 17.2,
title: 'Drop into the same orbit',
body: 'Oct 12: it dives 47 km and lands about <b>480 km behind</b>.',
},
{
s0: 17.2, s1: 22.0,
title: 'Speed up, then brake',
body: 'Oct 15: a 2 km dip makes it faster — closing <b>~350 km a day</b> — then it brakes 3 km behind.',
},
{
s0: 22.0, s1: 29.7,
title: 'Park behind it. Creep closer.',
body: 'Each orbit its path loops <b>1.6 km</b>, inching in for two days.',
},
{
s0: 29.7, s1: 32.3,
title: 'Into Earth\'s shadow',
body: 'Closing at strolling pace (<b>0.9 m/s</b>) — while both fly at 27,000 km/h.',
},
{
s0: 32.3, s1: 35.6,
title: `Closest pass: <span class="c-cos">~${CA_M} m</span>`,
body: 'Shown at true size. Published estimates: <b>150–650 m</b>.',
},
{
s0: 35.6, s1: 41.2,
title: 'Back off — and wait',
body: 'It burns away. Today it sits <b>480 km behind</b>, in the same orbit.',
},
cap('hook', `A Russian military satellite came within <span class="c-cos nw">~${CA_M} m</span> of a U.S.-registered one`,
'This is how it got there, rebuilt from public tracking data.', { hook: true, s0: -1 }),
cap('climb', 'Climb higher to fall behind',
'Same orbit, a quarter-orbit apart. Two burns lift <b class="c-cos">Cosmos 2612</b> <b>47 km</b>, and a higher orbit is a slower one.'),
cap('lap', 'Fall behind, and lap it',
'Drifting back <b>~6,700 km a day</b>, it laps almost twice, passing over <b class="c-x36">X36</b> on Oct 5 and Oct 12, as its orbit plane swings into line.'),
cap('drop', 'Drop into the same orbit',
'Oct 12: from <b>47 km</b> directly overhead it dives into X36\'s orbit, and lands about <b>480 km behind</b>.'),
cap('close', 'Speed up, then brake',
'A 2 km dip makes it faster, closing <b>~350 km a day</b>. Then it brakes 3 km behind.'),
cap('dance', 'The dance',
'Every orbit is a day and a night. Each one, Cosmos loops <b>~1.5 km</b> around X36, 3 km behind it.'),
cap('dance-ff', 'Flatten the loops',
'A tiny burn puts Cosmos almost exactly in X36\'s path. It creeps in <b>~3 km a day</b>.'),
cap('approach', 'Closing in, slowly',
'Creeping in at <b>a few centimetres per second</b>, while both fly at <b>27,000 km/h</b>.'),
cap('ca', `Closest pass: <span class="c-cos nw">~${CA_M} m</span>`,
'True size. LeoLabs measured <b>~500 m</b>; other estimates run <b>150 to 650 m</b>.'),
cap('sunrise', 'Then the sun comes up',
'Twenty minutes later both leave Earth\'s shadow. Within hours, Cosmos backs away.'),
cap('leave', 'Back off, and wait',
'It drifts back <b>~350 km a day</b>. By Oct 25 it sat <b>~480 km behind</b>, in the same orbit.'),
];
const BURN_NOTES = {
'Phasing raise': 'raises its orbit 46 km',
'Plane trim': 'tilts its orbit into line',
'Descent to X36 orbit': 'drops 47 km',
'Descent to X36 orbit': 'drops 47 km into X36\'s orbit',
'Closing burn': 'dips 2.3 km to speed up',
'Braking burn': 'rises 2.5 km to stop',
'Back-off': 'rises 130 m',
'Back-off': 'rises 140 m and starts to drift back',
'Ellipse trim': 'flattens its loops',
'Departure burn': 'rises 2.4 km to drift away',
'Re-match orbit': 'drops 2.6 km to hold position',
};
@@ -75,11 +65,12 @@ export function createHud(ctx) {
t: b.t, kind: 'burn', title: 'Engine burn',
sub: `${BURN_NOTES[b.label] || b.label} · Δv ${b.dv < 1 ? b.dv.toFixed(1) : Math.round(b.dv)} m/s`,
})),
{ t: sec('2026-10-05T11:55:00Z'), kind: 'note', title: 'Passes overhead', sub: '~50 km above its target' },
{ t: sec('2026-10-12T00:10:00Z'), kind: 'note', title: 'Directly overhead', sub: '47 km above its target' },
{ t: sec('2026-10-05T12:28:00Z'), kind: 'note', title: 'Passes overhead', sub: '55 km above its target' },
{ t: sec('2026-10-12T00:30:00Z'), kind: 'note', title: 'Directly overhead', sub: '54 km above its target' },
{ t: VANTOR_T, kind: 'vantor', title: 'Vantor photographs the pair', sub: `from its own satellite · ~${D.vantor.km.toFixed(0)} km apart · Oct 17` },
{ t: CA_T, kind: 'ca', title: 'Closest approach', sub: `${D.ca.t.slice(11, 16)} UTC, Oct 18` },
].map((e) => ({ ...e, s: sOfTau(e.t) })).sort((a, b) => a.s - b.s);
{ t: CA_T, kind: 'ca', title: 'Closest approach', sub: `${D.ca.t.slice(11, 16)} UTC, Oct 18 · in Earth's shadow` },
].map((e) => ({ ...e, s: sOfTau(e.t) })).filter((e) => e.s !== null).sort((a, b) => a.s - b.s);
const caS = EVENTS.find((e) => e.kind === 'ca')?.s ?? 1e9;
// -------------------------------------------------------------------------
// DOM
@@ -92,54 +83,40 @@ export function createHud(ctx) {
leaders.setAttribute('viewBox', `0 0 ${W} ${H}`);
$('#chapters').innerHTML = CHAPTERS.map((c) => `<div class="ch" data-k="${c.key}"><i><b></b></i><span>${c.label}</span></div>`).join('');
// Distance-over-time strip (log scale), drawn once, revealed by a clip.
const CH = { t0: sec('2026-09-30T00:00:00Z'), t1: sec('2026-10-21T18:00:00Z'), lo: 0.1, hi: 20000 };
const chartBox = $('#chart');
const chart = (() => {
const cw = chartBox.clientWidth;
const ch = chartBox.clientHeight;
const padL = (PORTRAIT ? 120 : 132) * U;
const padT = 10 * U;
const padB = 40 * U;
const x = (t) => padL + ((t - CH.t0) / (CH.t1 - CH.t0)) * (cw - padL);
const y = (km) =>
padT + (1 - (Math.log10(km) - Math.log10(CH.lo)) / (Math.log10(CH.hi) - Math.log10(CH.lo))) * (ch - padT - padB);
const NS = D.sep_km.length;
const cols = Math.round((cw - padL) / 2);
let lo = '';
for (let c = 0; c <= cols; c++) {
const ta = CH.t0 + (c / cols) * (CH.t1 - CH.t0);
const tb = CH.t0 + ((c + 1) / cols) * (CH.t1 - CH.t0);
let mn = Infinity;
for (let i = Math.floor(ta / D.dt); i <= Math.ceil(tb / D.dt); i++) mn = Math.min(mn, D.sep_km[Math.min(Math.max(i, 0), NS - 1)]);
lo += `${c ? 'L' : 'M'}${(padL + c * 2).toFixed(1)},${y(Math.max(mn, CH.lo)).toFixed(1)}`;
}
// Linear 22-day timeline with event markers (replaces the log chart).
const TL = { t0: sec('2026-09-30T00:00:00Z'), t1: sec('2026-10-21T23:59:00Z') };
const tlBox = $('#timeline');
const timeline = (() => {
const cw = tlBox.clientWidth;
const ch = tlBox.clientHeight;
const y = ch * 0.42;
const x = (t) => ((t - TL.t0) / (TL.t1 - TL.t0)) * cw;
const fs = (PORTRAIT ? 24 : 26) * U;
const grid = [[10000, '10,000 km'], [100, '100 km'], [1, '1 km']]
.map(([v, lab]) => `<line x1="${padL}" x2="${cw}" y1="${y(v)}" y2="${y(v)}" stroke="rgba(238,242,246,0.14)" stroke-width="${1.5 * U}"/>
<text x="${padL - 14 * U}" y="${y(v) + fs * 0.35}" text-anchor="end" font-size="${fs}" fill="rgba(238,242,246,0.6)">${lab}</text>`).join('');
const dates = [['2026-09-30', 'SEP 30'], ['2026-10-12', 'OCT 12'], ['2026-10-18', 'OCT 18']]
.map(([d, lab], k) => `<text x="${x(sec(`${d}T00:00:00Z`))}" y="${ch - 6 * U}" text-anchor="${k ? 'middle' : 'start'}" font-size="${fs}" fill="rgba(238,242,246,0.6)">${lab}</text>`).join('');
const burns = BURNS.map((b) => `<circle class="burn" data-t="${b.t}" cx="${x(b.t)}" cy="${padT + 2 * U}" r="${5 * U}" fill="#ffb547"/>`).join('');
chartBox.innerHTML = `<svg viewBox="0 0 ${cw} ${ch}">
<defs><clipPath id="past"><rect id="pastRect" x="0" y="-40" width="0" height="${ch + 80}"/></clipPath></defs>
${grid}${dates}${burns}
<path d="${lo}" fill="none" stroke="rgba(255,90,60,0.28)" stroke-width="${2.5 * U}"/>
<g clip-path="url(#past)"><path d="${lo}" fill="none" stroke="#ff5a3c" stroke-width="${4 * U}" stroke-linejoin="round"/></g>
<line id="playhead" x1="0" x2="0" y1="${padT}" y2="${ch - padB}" stroke="#fff" stroke-opacity="0.7" stroke-width="${2 * U}"/>
<circle id="playdot" r="${8 * U}" fill="#fff"/>
let ticks = '';
for (let d = 0; d <= 21; d++) {
const t = sec(`2026-${d === 0 ? '09-30' : `10-${String(d).padStart(2, '0')}`}T00:00:00Z`);
ticks += `<line x1="${x(t)}" x2="${x(t)}" y1="${y - 6 * U}" y2="${y + 6 * U}" stroke="rgba(244,247,250,0.3)" stroke-width="${1.5 * U}"/>`;
}
const labs = [['2026-09-30', 'SEP 30', 'start'], ['2026-10-12', 'OCT 12', 'middle'], ['2026-10-18', 'OCT 18', 'middle'], ['2026-10-21T23:59:00Z', 'OCT 21', 'end']]
.map(([d, l, a]) => `<text x="${x(sec(d.length > 10 ? d : `${d}T00:00:00Z`))}" y="${ch - 2 * U}" text-anchor="${a}" font-size="${fs}" fill="rgba(244,247,250,0.66)">${l}</text>`).join('');
const burns = BURNS.map((b) => `<circle class="burn" data-t="${b.t}" cx="${x(b.t)}" cy="${y}" r="${7 * U}" fill="#ffb547"/>`).join('');
const ca = `<g class="camk" data-t="${CA_T}"><circle cx="${x(CA_T)}" cy="${y}" r="${10 * U}" fill="none" stroke="#fff" stroke-width="${3 * U}"/></g>`;
tlBox.innerHTML = `<svg viewBox="0 0 ${cw} ${ch}">
<line x1="0" x2="${cw}" y1="${y}" y2="${y}" stroke="rgba(244,247,250,0.22)" stroke-width="${4 * U}" stroke-linecap="round"/>
<line id="tlDone" x1="0" x2="0" y1="${y}" y2="${y}" stroke="#ff5a3c" stroke-width="${4 * U}" stroke-linecap="round"/>
${ticks}${burns}${ca}${labs}
<circle id="tlHead" cx="0" cy="${y}" r="${11 * U}" fill="#fff"/>
</svg>`;
return { x, y };
return { x };
})();
function tag(id, name, meta, cls) {
function tag(id, name, cls) {
let el = document.getElementById(id);
if (!el) {
el = document.createElement('div');
el.id = id;
el.className = 'tag';
el.dataset.meta = meta;
el.innerHTML = `<div class="name ${cls}">${name}</div><div class="meta">${meta}</div>`;
el.innerHTML = `<div class="name ${cls}">${name}</div><div class="meta"></div>`;
tagsEl.appendChild(el);
}
return el;
@@ -154,16 +131,25 @@ export function createHud(ctx) {
function perSecond(r) {
if (r < 90) return `${Math.round(r)} sec`;
if (r < 5400) return `${Math.round(r / 60)} min`;
if (r < 129600) return `${(r / 3600).toFixed(r < 36000 ? 1 : 0)} hours`.replace('1.0 hours', '1 hour');
if (r < 129600) return `${(r / 3600).toFixed(r < 36000 ? 1 : 0)} hours`.replace(/^1\.0 hours$/, '1 hour');
return `${(r / 86400).toFixed(1)} days`.replace('.0 days', ' days');
}
// Label placement memory (hysteresis).
const memo = { tagX: null, tagC: null, lastS: -1 };
// -------------------------------------------------------------------------
// Per-frame update
// -------------------------------------------------------------------------
function update(s, t, b, st) {
const endA = fade(s, 41.2, 99, 0.5);
const sh = shotAt(s);
const endA = fade(s, END, 99, 0.5);
const live = 1 - endA;
if (Math.abs(s - memo.lastS) > 0.2 || CUTS.some((c) => memo.lastS < c && s >= c)) {
memo.tagX = null;
memo.tagC = null;
}
memo.lastS = s;
// Chapters
for (const c of CHAPTERS) {
@@ -172,137 +158,109 @@ export function createHud(ctx) {
el.classList.toggle('on', s >= c.s0 && s < c.s1);
el.querySelector('b').style.width = `${p * 100}%`;
}
$('#chapters').style.opacity = fade(s, 4.2, 41.2, 0.4);
$('#chapters').style.opacity = fade(s, CHAPTERS[0].s0 - 0.2, END, 0.4);
// Caption (one at a time, quick crossfade)
let cap = null;
// Caption
let cur = null;
let capA = 0;
for (const c of CAPTIONS) {
const a = fade(s, c.s0, c.s1, 0.3);
if (a > capA) {
cap = c;
cur = c;
capA = a;
}
}
if (cap) {
capEl.classList.toggle('hook', !!cap.hook);
capEl.querySelector('h1').innerHTML = cap.title;
capEl.querySelector('p').innerHTML = cap.body;
if (cur) {
capEl.classList.toggle('hook', !!cur.hook);
capEl.querySelector('h1').innerHTML = cur.title;
capEl.querySelector('p').innerHTML = cur.body;
}
capEl.style.opacity = capA * live;
capEl.style.transform = `translateY(${(1 - capA) * 14 * U}px)`;
// Clock + speed
// "22 days earlier" interstitial after the cold open
const climbS = span('climb')[0];
const inter = $('#interstitial');
inter.style.opacity = fade(s, climbS - 0.05, climbS + 1.3, 0.25);
// Clock, speed, mode, light
const d = new Date(Date.parse(D.t0) + t * 1000);
const mon = d.toLocaleString('en-US', { month: 'short', timeZone: 'UTC' }).toUpperCase();
$('#clock .date').textContent = `${mon} ${d.getUTCDate()}`;
$('#clock .time').textContent = `${d.toISOString().slice(11, 16)} UTC`;
$('#clock .rate').innerHTML = `1 sec = ${perSecond(rate(s))}`;
$('#clock .ff').style.display = sh.mode === 'ff' ? 'inline-block' : 'none';
const lit = st.lx > 0.5;
const light = $('#clock .light');
light.className = `light ${lit ? 'day' : 'night'}`;
light.innerHTML = lit ? '<i></i>In sunlight' : '<i></i>In Earth\'s shadow';
light.style.display = sh.mode === 'true' && sh.id !== 'climb' && sh.id !== 'end' ? 'inline-flex' : 'none';
$('#clock').style.opacity = live;
// Distance readout
const ro = $('#readout');
ro.style.opacity = fade(s, 4.4, 41.2, 0.4);
ro.style.opacity = fade(s, climbS + 0.6, END, 0.4);
ro.querySelector('.val').innerHTML = fmtSep(st.sep);
const [r, tt] = st.rel;
const [r, tt0] = st.rel;
const circ = 2 * Math.PI * D.a_ref_km * 1000;
const tt = ((tt0 % circ) + circ * 1.5) % circ - circ / 2;
let ctxLine;
const caS = EVENTS.find((e) => e.kind === 'ca').s;
if (st.sep < 1500) ctxLine = `≈ ${Math.max(1, Math.round(st.sep / 100))} football fields`;
else if (st.sep < 2e6) ctxLine = `${Math.abs(tt) >= 1e4 ? fmtNum(Math.abs(tt) / 1000) + ' km' : (Math.abs(tt) / 1000).toFixed(1) + ' km'} ${tt < 0 ? 'behind' : 'ahead'}${Math.abs(r) > 20e3 ? ` · ${fmtNum(Math.abs(r) / 1000)} km ${r > 0 ? 'higher' : 'lower'}` : ''}`;
else ctxLine = `${Math.abs(r) > 20e3 ? `${fmtNum(Math.abs(r) / 1000)} km higher · ` : ''}far side of the orbit`;
if (s > caS && s < 36.5) ctxLine += ` · closest ${CA_M} m`;
else ctxLine = `${Math.abs(r) > 20e3 ? `${fmtNum(Math.abs(r) / 1000)} km higher · ` : ''}a quarter-orbit or more apart`;
if (s > caS && s < span('leave')[0] + 0.5) ctxLine += ` · closest ${CA_M} m`;
ro.querySelector('.ctx').textContent = ctxLine;
const relLine = ro.querySelector('.rel');
relLine.textContent = st.sep < 2e4 && sh.mode === 'true' ? (st.relSpeed < 0.2 ? `relative speed ${Math.round(st.relSpeed * 100)} cm/s` : `relative speed ${st.relSpeed.toFixed(1)} m/s`) : '';
// Scale chip: amber when enlarged, white at true size.
// Scale chip
const magEl = $('#mag');
const enlarged = st.mag > 1.5;
const trueSize = !enlarged && st.camDistX < 200;
magEl.style.opacity = (enlarged || trueSize) ? fade(s, 4.4, 41.2, 0.3) : 0;
magEl.style.opacity = (enlarged || trueSize) ? fade(s, climbS, END, 0.3) : 0;
magEl.classList.toggle('enlarged', enlarged);
magEl.textContent = enlarged ? `Satellites enlarged ${Math.round(st.mag)}×` : 'True size';
// Chart
const px = chart.x(Math.min(Math.max(t, CH.t0), CH.t1));
document.getElementById('pastRect').setAttribute('width', px);
const ph = document.getElementById('playhead');
ph.setAttribute('x1', px);
ph.setAttribute('x2', px);
const pd = document.getElementById('playdot');
pd.setAttribute('cx', px);
pd.setAttribute('cy', chart.y(Math.max(sepAt(t), CH.lo)));
for (const g of chartBox.querySelectorAll('.burn')) g.style.opacity = Number(g.dataset.t) <= t ? 1 : 0.3;
$('#chartwrap').style.opacity = fade(s, 4.4, 41.2, 0.4);
// Timeline
const px = timeline.x(Math.min(Math.max(t, TL.t0), TL.t1));
document.getElementById('tlHead').setAttribute('cx', px);
document.getElementById('tlDone').setAttribute('x2', px);
for (const g of tlBox.querySelectorAll('.burn')) g.style.opacity = Number(g.dataset.t) <= t ? 1 : 0.35;
tlBox.querySelector('.camk').style.opacity = t >= CA_T - 60 ? 1 : 0.4;
$('#tlwrap').style.opacity = fade(s, climbS + 0.6, END, 0.4);
// Event toasts (max two, newest on top)
// Event toasts (max two, newest first)
let html = '';
const quiet = s > 31.85 && s < 33.95; // true-size shot: headline + pulse carry it
if (!quiet && st.lx < 0.5 && st.sep < 5e3 && s < 41.2) html += '<div class="event shadow"><i></i><div><div class="t">In Earth\'s shadow</div><div class="sub">orbital night · sunrise in minutes</div></div></div>';
const active = EVENTS.map((e) => ({ e, a: fade(s, e.s, e.s + (e.kind === 'ca' ? 3.6 : e.kind === 'vantor' ? 2.8 : 2.0), 0.2) }))
const quiet = sh.id === 'ca';
const active = EVENTS.map((e) => ({ e, a: fade(s, e.s, e.s + (e.kind === 'ca' ? 3.0 : e.kind === 'vantor' ? 2.2 : 1.9), 0.2) }))
.filter((x) => x.a > 0).slice(-2).reverse();
for (const { e, a } of quiet ? [] : active) {
html += `<div class="event ${e.kind}" style="opacity:${a * live}"><i></i><div><div class="t">${e.title}</div><div class="sub">${e.sub}</div></div></div>`;
}
evEl.innerHTML = html;
// Satellite tags + in-scene dimension line
// Satellite tags with speeds, dimension line, pulse
const pX = project(st.x36W);
const pC = project(st.cosW);
const tX = tag('tagX', 'ICEYE-X36', 'U.S.-registered radar satellite', 'c-x36');
const tC = tag('tagC', 'COSMOS 2612', 'Russian military', 'c-cos');
const tagA = fade(s, 4.0, 41.2, 0.4);
const tX = tag('tagX', 'ICEYE-X36', 'c-x36');
const tC = tag('tagC', 'COSMOS 2612', 'c-cos');
const tagA = fade(s, span('climb')[0] + 0.6, END, 0.4);
const onScreen = (p) => p && p.x > -40 && p.y > -40 && p.x < W + 40 && p.y < H + 40;
const speedX = `${(st.vX / 1000).toFixed(2)} km/s`;
const speedC = `${(st.vC / 1000).toFixed(2)} km/s`;
tX.querySelector('.meta').textContent = pX?.hidden ? 'far side of Earth' : `U.S.-registered radar sat · ${speedX}`;
tC.querySelector('.meta').textContent = pC?.hidden ? 'far side of Earth' : `Russian military · ${speedC}`;
let svg = '';
// Tags try four quadrants around their satellite and take the first that
// stays on screen and clear of HUD text; the other tag counts too.
const M = 24 * U;
const obstacles = [];
for (const q of ['#logo', '#clock', '#caption', '#readout', '#chartwrap', '#chapters', '#mag']) {
for (const q of ['#logo', '#clock', '#caption', '#readout', '#tlwrap', '#chapters', '#mag']) {
const el = $(q);
if (Number(el.style.opacity || 1) > 0.05) obstacles.push(el.getBoundingClientRect());
}
for (const el of evEl.children) obstacles.push(el.getBoundingClientRect());
const overlap = (r) => obstacles.reduce((acc, o) => acc + Math.max(0, Math.min(r.r, o.right + 8 * U) - Math.max(r.l, o.left - 8 * U)) * Math.max(0, Math.min(r.b, o.bottom + 8 * U) - Math.max(r.t, o.top - 8 * U)), 0);
const hits = (r) => obstacles.some((o) => r.l < o.right + 8 * U && r.r > o.left - 8 * U && r.t < o.bottom + 8 * U && r.b > o.top - 8 * U);
const place = (el, p, prefer, color) => {
if (!onScreen(p)) {
el.style.opacity = 0;
return;
}
const a = tagA;
el.style.opacity = a;
el.querySelector('.meta').textContent = p.hidden ? 'far side of Earth' : el.dataset.meta;
const w = el.offsetWidth;
const h = el.offsetHeight;
const quads = [prefer, [prefer[0], -prefer[1]], [-prefer[0], prefer[1]], [-prefer[0], -prefer[1]]];
let best = null;
for (const [sx, sy] of quads) {
const lx = p.x + sx * 66 * U;
const ly = p.y + sy * 56 * U;
const l = sx > 0 ? lx + 10 * U : lx - 10 * U - w;
const r = { l, r: l + w, t: ly - h / 2, b: ly + h / 2 };
const inside = r.l > M && r.r < W - M && r.t > M && r.b < H - M;
const cand = { sx, sy, lx, ly, r };
cand.cost = (inside ? 0 : 1e9) + overlap(r);
if (!best || cand.cost < best.cost) best = cand;
if (cand.cost === 0) break;
}
if (best.cost >= 1e9) {
const [sx, sy] = prefer;
const lx = p.x + sx * 66 * U;
const ly = p.y + sy * 56 * U;
let l = sx > 0 ? lx + 10 * U : lx - 10 * U - w;
l = Math.min(Math.max(l, M), W - M - w);
best = { sx, sy, lx, ly, r: { l, r: l + w, t: ly - h / 2, b: ly + h / 2 } };
}
el.style.left = `${best.r.l}px`;
el.style.top = `${best.r.t}px`;
el.style.transform = 'none';
el.style.textAlign = best.sx > 0 ? 'left' : 'right';
obstacles.push({ left: best.r.l, right: best.r.r, top: best.r.t, bottom: best.r.b });
svg += `<line x1="${p.x + best.sx * 12 * U}" y1="${p.y + best.sy * 12 * U}" x2="${best.lx}" y2="${best.ly}" stroke="${color}" stroke-opacity="${a * 0.85}" stroke-width="${2.5 * U}"/>`;
svg += `<circle cx="${p.x}" cy="${p.y}" r="${15 * U}" fill="none" stroke="${color}" stroke-opacity="${a * 0.9}" stroke-width="${2.5 * U}"/>`;
};
// Dimension line + pill (offset perpendicular so it clears the line's ends).
// Dimension line + pill
const dim = $('#dim');
const gap = onScreen(pX) && onScreen(pC) ? Math.hypot(pC.x - pX.x, pC.y - pX.y) : 0;
const dimA = st.sep < 60e3 && gap > 160 * U ? tagA : 0;
@@ -310,7 +268,7 @@ export function createHud(ctx) {
svg += `<line x1="${pX.x}" y1="${pX.y}" x2="${pC.x}" y2="${pC.y}" stroke="#fff" stroke-opacity="${0.75 * dimA}" stroke-width="${2.5 * U}" stroke-dasharray="${10 * U} ${8 * U}"/>`;
const nx = -(pC.y - pX.y) / gap;
const ny = (pC.x - pX.x) / gap;
const side = ny > 0 ? -1 : 1; // put the pill above the line
const side = ny > 0 ? -1 : 1;
dim.innerHTML = fmtSep(st.sep).replace(/<\/?small>/g, ' ').trim();
const mx = (pX.x + pC.x) / 2 + side * nx * 40 * U;
const my = (pX.y + pC.y) / 2 + side * ny * 40 * U;
@@ -321,15 +279,66 @@ export function createHud(ctx) {
obstacles.push({ left: mx - w / 2, right: mx + w / 2, top: my - h / 2, bottom: my + h / 2 });
}
dim.style.opacity = dimA;
// Pulse rings on both satellites at the closest moment.
const caS2 = EVENTS.find((e) => e.kind === 'ca').s;
const k = (s - caS2) / 1.2;
const k = (s - caS) / 1.2;
if (k > 0 && k < 1) {
for (const p of [pX, pC]) {
if (!onScreen(p)) continue;
svg += `<circle cx="${p.x}" cy="${p.y}" r="${(18 + 90 * k) * U}" fill="none" stroke="#fff" stroke-opacity="${(1 - k) * 0.9}" stroke-width="${4 * U}"/>`;
}
}
// Tags: keep the previous quadrant unless it has been blocked for a while;
// ease the label toward its target so it never jumps.
const place = (key, el, p, prefer, color) => {
if (!onScreen(p)) {
el.style.opacity = 0;
memo[key] = null;
return;
}
el.style.opacity = tagA;
const w = el.offsetWidth;
const h = el.offsetHeight;
const rectFor = ([sx, sy]) => {
const lx = p.x + sx * 66 * U;
const ly = p.y + sy * 56 * U;
const l = sx > 0 ? lx + 10 * U : lx - 10 * U - w;
const r = { l, r: l + w, t: ly - h / 2, b: ly + h / 2 };
const inside = r.l > M && r.r < W - M && r.t > M && r.b < H - M;
return { sx, sy, lx, ly, r, cost: (inside ? 0 : 1e9) + overlap(r) };
};
const m = memo[key];
let q = m ? [m.sx, m.sy] : prefer;
let cand = rectFor(q);
if (m) {
m.blocked = cand.cost > 2000 * U * U ? m.blocked + 1 : 0;
}
if (!m || m.blocked > 8) {
const quads = [q, prefer, [prefer[0], -prefer[1]], [-prefer[0], prefer[1]], [-prefer[0], -prefer[1]]];
for (const qq of quads) {
const c = rectFor(qq);
if (c.cost < cand.cost - 1) cand = c;
}
q = [cand.sx, cand.sy];
}
// Ease: dx/dy offsets glide (state), anchor follows the satellite exactly.
const target = { dx: cand.r.l - p.x, dy: cand.r.t - p.y };
const prev = m && m.dx !== undefined ? m : null;
const e = prev ? 0.3 : 1;
const dx = prev ? prev.dx + (target.dx - prev.dx) * e : target.dx;
const dy = prev ? prev.dy + (target.dy - prev.dy) * e : target.dy;
memo[key] = { sx: q[0], sy: q[1], blocked: m && m.sx === q[0] && m.sy === q[1] ? m.blocked : 0, dx, dy };
const left = Math.min(Math.max(p.x + dx, M), W - M - w);
const top = Math.min(Math.max(p.y + dy, M), H - M - h);
el.style.left = `${left}px`;
el.style.top = `${top}px`;
el.style.transform = 'none';
el.style.textAlign = q[0] > 0 ? 'left' : 'right';
obstacles.push({ left, right: left + w, top, bottom: top + h });
const ax = q[0] > 0 ? left - 6 * U : left + w + 6 * U;
const ay = top + h * 0.32;
svg += `<line x1="${p.x + Math.sign(ax - p.x) * 14 * U}" y1="${p.y + Math.sign(ay - p.y) * 10 * U}" x2="${ax}" y2="${ay}" stroke="${color}" stroke-opacity="${tagA * 0.85}" stroke-width="${2.5 * U}"/>`;
svg += `<circle cx="${p.x}" cy="${p.y}" r="${15 * U}" fill="none" stroke="${color}" stroke-opacity="${tagA * 0.9}" stroke-width="${2.5 * U}"/>`;
};
let pX0 = [1, -1];
let pC0 = [-1, 1];
if (onScreen(pX) && onScreen(pC)) {
@@ -338,17 +347,13 @@ export function createHud(ctx) {
pX0 = [sx, sy];
pC0 = [-sx, -sy];
}
place(tX, pX, pX0, '#5ce1e6');
place(tC, pC, pC0, '#ff5a3c');
place('tagX', tX, pX, pX0, '#5ce1e6');
place('tagC', tC, pC, pC0, '#ff5a3c');
leaders.innerHTML = svg;
// Burn flash ring on Cosmos
// (drawn by the scene; nothing here)
// Dip at cuts
let dip = 0;
for (const c of CUTS) dip = Math.max(dip, 1 - Math.abs(s - c) / 0.14);
for (const c of CUTS) dip = Math.max(dip, 1 - Math.abs(s - c) / 0.12);
$('#dip').style.opacity = Math.max(0, dip);
// End card
@@ -359,17 +364,18 @@ export function createHud(ctx) {
ec.dataset.built = '1';
ec.innerHTML = `<div class="panel">
<div class="big">~${CA_M} m</div>
<div class="range">estimated · published range 150–650 m</div>
<div class="range">rebuilt from public element sets · LeoLabs ~500 m · published range 150–650 m</div>
<div class="lead">Closest pass between Cosmos 2612 and ICEYE-X36 · Oct 18, in Earth's shadow</div>
<div class="stats">
<div><b>22</b><span>days in pursuit</span></div>
<div><b>8</b><span>engine burns</span></div>
<div><b>0.9 m/s</b><span>speed at closest pass</span></div>
<div><b>${BURNS.length}</b><span>engine burns</span></div>
<div><b>${Math.max(1, Math.round((D.ca.v || 0.05) * 100))} cm/s</b><span>relative speed at closest pass</span></div>
</div>
</div>`;
}
$('#endlogo').style.opacity = endA;
$('#logo').style.opacity = live;
void DUR;
}
return { update };
+305
View File
@@ -0,0 +1,305 @@
#!/usr/bin/env python3
"""Data-driven reconstruction of the Cosmos 2612 / ICEYE-X36 encounter (v4).
Inputs: the public element-set history in tle/ (Space-Track catalog via the
OMPub/RSO daily archive and jazzpi/ssa; see tle/SOURCES.md).
Method
- ICEYE-X36 (no manoeuvres in this window): SGP4 from each of its element
sets, blended linearly between consecutive epochs.
- Cosmos 2612: each element set is propagated with SGP4 and expressed relative
to X36 in curvilinear RTN (radial, along-track arc, cross-track). Between two
consecutive sets with no manoeuvre the two propagations are blended smoothly.
Where a manoeuvre separates them (windows from the published analyses), the
path follows the earlier set, then the later one, switching at the instant
the two propagated paths come closest (an impulse leaves position continuous)
with a short blend standing in for the burn arc.
- The closest approach is read off the stitched path. Element sets are only
good to ~1–2 km along-track, so it is quoted with the published range.
Output: data.json (same schema as model.py) consumed by scene.js.
"""
import json
import math
from datetime import datetime, timedelta, timezone
import numpy as np
from sgp4.api import Satrec, jday
HERE = __file__.rsplit('/', 1)[0]
MU = 398600.4418
RE = 6378.137
UTC = timezone.utc
T0 = datetime(2026, 9, 29, 12, 0, tzinfo=UTC)
T1 = datetime(2026, 10, 22, 12, 0, tzinfo=UTC)
DT = 60
def utc(s):
return datetime.fromisoformat(s.replace('Z', '+00:00'))
def load(path):
L = [l.rstrip('\n') for l in open(path) if l.strip()]
out = []
for i in range(len(L) - 1):
if L[i].startswith('1 ') and L[i + 1].startswith('2 '):
s = Satrec.twoline2rv(L[i], L[i + 1])
ep = datetime(2000, 1, 1, 12, tzinfo=UTC) + timedelta(days=s.jdsatepoch + s.jdsatepochF - 2451545.0)
out.append((ep, s))
out.sort(key=lambda x: x[0])
# Re-issued fits of the same epoch: keep the last one.
ded = []
for ep, s in out:
if ded and abs((ep - ded[-1][0]).total_seconds()) < 60:
ded[-1] = (ep, s)
else:
ded.append((ep, s))
return ded
def sgp(s, d):
jd, fr = jday(d.year, d.month, d.day, d.hour, d.minute, d.second + d.microsecond / 1e6)
e, r, v = s.sgp4(jd, fr)
return np.array(r), np.array(v)
def sma(s):
return (MU / (s.no_kozai / 60) ** 2) ** (1 / 3)
X = load(f'{HERE}/tle/59103.tle')
C = load(f'{HERE}/tle/68762.tle')
def x36(d):
"""X36 TEME state, blended between the bracketing element sets."""
i = max(0, min(len(X) - 2, max(k for k in range(len(X)) if X[k][0] <= d) if d >= X[0][0] else 0))
(ea, sa), (eb, sb) = X[i], X[i + 1]
w = min(1, max(0, (d - ea).total_seconds() / (eb - ea).total_seconds()))
ra, va = sgp(sa, d)
rb, vb = sgp(sb, d)
return ra + (rb - ra) * w, va + (vb - va) * w
def frame(r, v):
R = r / np.linalg.norm(r)
N = np.cross(r, v)
N /= np.linalg.norm(N)
return R, np.cross(N, R), N
A_REF = 6920.0 # km; along-track is stored as phase angle x A_REF so laps don't pick up radius wobble
def rel_of(rx, vx, rc):
"""Curvilinear RTN (m): radius difference, along-track (phase x A_REF), cross-track."""
R, T, N = frame(rx, vx)
th = math.atan2(np.dot(np.cross(rx, rc), N), np.dot(rx, rc))
return np.array([np.linalg.norm(rc) - np.linalg.norm(rx), th * A_REF, np.dot(rc, N)]) * 1000
def rel_from(k, d):
rx, vx = x36(d)
rc, _ = sgp(C[k][1], d)
return rel_of(rx, vx, rc), rx
# Manoeuvre windows between element sets (UTC). Burn time is solved inside each.
BURNS = [
('Phasing raise', '2026-09-30T06:00:00Z', '2026-09-30T16:00:00Z', 48),
('Descent to X36 orbit', '2026-10-12T00:30:00Z', '2026-10-12T05:00:00Z', 48),
('Closing burn', '2026-10-15T01:37:00Z', '2026-10-15T15:56:00Z', 20),
('Braking burn', '2026-10-16T04:40:00Z', '2026-10-16T14:14:00Z', 20),
('Ellipse trim', '2026-10-16T19:00:00Z', '2026-10-17T07:45:00Z', 10),
('Back-off', '2026-10-17T22:00:00Z', '2026-10-18T05:00:00Z', 6),
('Departure burn', '2026-10-18T12:25:00Z', '2026-10-18T23:34:00Z', 20),
('Re-match orbit', '2026-10-19T07:32:00Z', '2026-10-20T07:26:00Z', 20),
]
def seg_index(d):
return max(0, min(len(C) - 2, max(k for k in range(len(C)) if C[k][0] <= d) if d >= C[0][0] else 0))
def unwrap_to(ref, val, circ):
while val - ref > circ / 2:
val -= circ
while val - ref < -circ / 2:
val += circ
return val
# Solve each burn's switch time: where the two neighbouring propagations meet.
solved = []
for name, a, b, blend_min in BURNS:
ta, tb = utc(a), utc(b)
k = seg_index(ta + (tb - ta) / 2)
best = None
t = ta
while t <= tb:
r1, rx = rel_from(k, t)
r2, _ = rel_from(k + 1, t)
circ = 2 * math.pi * A_REF * 1000
r2[1] = unwrap_to(r1[1], r2[1], circ)
dd = np.linalg.norm(r1 - r2)
if best is None or dd < best[0]:
best = (dd, t)
t += timedelta(minutes=2)
xs = min(X, key=lambda x: abs((x[0] - best[1]).total_seconds()))[1] if best else X[0][1]
da0 = sma(C[k][1]) - sma(xs)
da1 = sma(C[k + 1][1]) - sma(xs)
di = math.degrees(C[k + 1][1].inclo - C[k][1].inclo)
xs = min(X, key=lambda x: abs((x[0] - best[1]).total_seconds()))[1]
da0 = sma(C[k][1]) - sma(xs)
da1 = sma(C[k + 1][1]) - sma(xs)
solved.append(dict(name=name, t=best[1], k=k, mismatch_m=best[0], blend=blend_min * 60, da0=da0, da1=da1, di=di))
print(f'{name:22} switch {best[1].isoformat()[:16]} path mismatch {best[0]:8.0f} m '
f'dSMA {da0:+8.3f} -> {da1:+8.3f} km di {di:+.3f} deg (sets {C[k][0].isoformat()[:16]} / {C[k+1][0].isoformat()[:16]})')
switch = {s['k']: s for s in solved}
def rel_at(d):
k = seg_index(d)
r1, rx = rel_from(k, d)
if k + 1 >= len(C):
return r1, rx
r2, _ = rel_from(k + 1, d)
circ = 2 * math.pi * A_REF * 1000
r2[1] = unwrap_to(r1[1], r2[1], circ)
if k in switch:
s = switch[k]
x = (d - s['t']).total_seconds() / s['blend'] + 0.5
else:
x = (d - C[k][0]).total_seconds() / (C[k + 1][0] - C[k][0]).total_seconds()
x = min(1, max(0, x))
w = x * x * (3 - 2 * x)
return r1 + (r2 - r1) * w, rx
times = [T0 + timedelta(seconds=i * DT) for i in range(int((T1 - T0).total_seconds() // DT) + 1)]
rel = []
x36p = []
prevT = None
for d in times:
r, rx = rel_at(d)
if prevT is not None:
r[1] = unwrap_to(prevT, r[1], 2 * math.pi * A_REF * 1000)
prevT = r[1]
rel.append(r)
x36p.append(rx)
rel = np.array(rel)
x36p = np.array(x36p)
A = float(np.mean([sma(s) for _, s in X]))
def true_sep(i):
rx = x36p[i]
d = times[i]
_, vx = x36(d)
R, T, N = frame(rx, vx)
th = rel[i][1] / 1000 / A_REF
Rp = R * math.cos(th) + T * math.sin(th)
pc = (np.linalg.norm(rx) + rel[i][0] / 1000) * Rp + rel[i][2] / 1000 * N
return np.linalg.norm(pc - rx)
sep = np.array([true_sep(i) for i in range(len(times))])
# Closest approach on the stitched path, refined at 5 s.
i0 = int(np.argmin(np.where([utc('2026-10-17T12:00:00Z') < d < utc('2026-10-18T12:00:00Z') for d in times], sep, 1e9)))
best = None
d = times[i0] - timedelta(minutes=20)
while d < times[i0] + timedelta(minutes=20):
r, _ = rel_at(d)
m = np.linalg.norm(r)
if best is None or m < best[0]:
best = (m, d, r)
d += timedelta(seconds=5)
ca_m, ca_t, ca_r = best
r1, _ = rel_at(ca_t + timedelta(seconds=30))
r0, _ = rel_at(ca_t - timedelta(seconds=30))
ca_v = float(np.linalg.norm(r1 - r0) / 60)
ecl = [times[i] for i in range(1, len(times)) if lit[i] != lit[i - 1] and abs((times[i] - ca_t).total_seconds()) < 3600] if 'lit' in dir() else []
print(f'CA {ca_t.isoformat()[:19]}Z {ca_m:.0f} m RTN {np.round(ca_r, 0)} rel speed {ca_v:.2f} m/s')
# Every pass within 1 km on Oct 17-18 (Cosmos may cross X36 more than once).
passes = []
win = [i for i, d in enumerate(times) if utc('2026-10-17T12:00:00Z') < d < utc('2026-10-18T12:00:00Z')]
for j in win[1:-1]:
if sep[j] < sep[j - 1] and sep[j] <= sep[j + 1] and sep[j] < 1.0:
passes.append((times[j].isoformat()[:16], round(float(sep[j]) * 1000)))
print('local minima < 1 km:', passes)
# Sunlit flag for X36 (cylindrical shadow).
def sun_dir(d):
n = (d - datetime(2000, 1, 1, 12, tzinfo=UTC)).total_seconds() / 86400
L = math.radians((280.46 + 0.9856474 * n) % 360)
g = math.radians((357.528 + 0.9856003 * n) % 360)
lam = L + math.radians(1.915) * math.sin(g) + math.radians(0.02) * math.sin(2 * g)
eps = math.radians(23.439)
return np.array([math.cos(lam), math.cos(eps) * math.sin(lam), math.sin(eps) * math.sin(lam)])
def sunlit(r, d):
s = sun_dir(d)
p = np.dot(r, s)
return p > 0 or np.linalg.norm(r - p * s) > RE
lit = np.array([sunlit(x36p[i], times[i]) for i in range(len(times))])
mask = np.array([utc('2026-10-17T06:00:00Z') < d < utc('2026-10-17T23:59:00Z') for d in times]) & lit
iv = int(np.argmin(np.where(mask, np.abs(sep - 1.02), 1e9)))
# Burns for the HUD: time, label, Δv estimate from the element-set changes.
V = math.sqrt(MU / A) * 1000
burn_list = []
for s in solved:
dvt = V * abs(s['da1'] - s['da0']) / A / 2
dvn = V * math.radians(abs(s['di']))
dv = math.hypot(dvt, dvn)
if s['name'] == 'Ellipse trim':
dv = max(dv, 0.4)
burn_list.append(dict(t=s['t'].isoformat().replace('+00:00', 'Z'), label=s['name'], dv=round(dv, 2)))
# The Oct 7 out-of-plane trim (ISR: ~4 m/s, inclination +0.031 deg).
burn_list.append(dict(t='2026-10-07T10:30:00Z', label='Plane trim', dv=4.0))
burn_list.sort(key=lambda b: b['t'])
total_dv = sum(b['dv'] for b in burn_list)
# SMA offset (km), stepping at the solved switch times.
def da_at(d):
k = seg_index(d)
xs = min(X, key=lambda x: abs((x[0] - d).total_seconds()))[1]
if k in switch and d >= switch[k]['t']:
k += 1
return sma(C[min(k, len(C) - 1)][1]) - sma(xs)
out = dict(
t0=T0.isoformat().replace('+00:00', 'Z'),
dt=DT,
a_km=A,
a_ref_km=A_REF,
x36=np.round(x36p, 4).ravel().tolist(),
rel=np.round(rel, 2).ravel().tolist(),
sep_km=np.round(sep, 4).tolist(),
burns=burn_list,
total_dv=round(total_dv, 1),
ca=dict(t=ca_t.isoformat().replace('+00:00', 'Z'), m=round(ca_m, 1), rtn=[round(v, 1) for v in ca_r], v=round(ca_v, 2),
shadow=[t.isoformat().replace('+00:00', 'Z') for t in [times[i] for i in range(1, len(times)) if lit[i] != lit[i - 1] and abs((times[i] - ca_t).total_seconds()) < 3600]]),
passes=passes,
vantor=dict(t=times[iv].isoformat().replace('+00:00', 'Z'), km=round(float(sep[iv]), 2)),
lit=''.join('1' if v else '0' for v in lit),
sma_offset_km=[round(da_at(times[i]), 3) for i in range(0, len(times), 10)],
source='public element sets (Space-Track via OMPub/RSO, jazzpi/ssa), stitched',
)
json.dump(out, open(f'{HERE}/data.json', 'w'), separators=(',', ':'))
print('burns:', [(b['t'][5:16], b['label'], b['dv']) for b in burn_list], 'total', round(total_dv, 1))
print('vantor', out['vantor'])
for iso in ['2026-09-30T08:00:00Z', '2026-10-05T12:00:00Z', '2026-10-12T00:10:00Z', '2026-10-13T00:00:00Z', '2026-10-15T12:00:00Z',
'2026-10-16T12:00:00Z', '2026-10-17T07:45:00Z', '2026-10-17T17:18:00Z', '2026-10-17T23:40:00Z', '2026-10-18T01:00:00Z',
'2026-10-18T03:00:00Z', '2026-10-18T07:38:00Z', '2026-10-19T07:32:00Z', '2026-10-21T12:00:00Z']:
i = int((utc(iso) - T0).total_seconds() // DT)
print(f' {iso} sep {sep[i]:10.3f} km rtn(km) {np.round(rel[i] / 1000, 3)}')
+2 -3
View File
@@ -35,8 +35,6 @@ const FORMATS = { '16x9': [1920, 1080], '9x16': [1080, 1920], preview: [960, 540
const format = args.format || '16x9';
const [W, H] = FORMATS[format];
const FPS = Number(args.fps || 30);
const DUR = 45;
const total = Math.round(DUR * FPS);
const TAG = args.tag ? String(args.tag) : '';
const RS = Number(args.rs || 1);
const outDir = join(ROOT, 'output/rpo-replay', TAG, format);
@@ -108,8 +106,9 @@ page.on('console', (m) => {
if (m.type() === 'error' || m.type() === 'warning') console.log(`[page ${m.type()}]`, m.text());
});
page.on('pageerror', (e) => console.log('[pageerror]', e.message));
await page.goto(`http://127.0.0.1:${port}/scene.html?w=${W}&h=${H}&rs=${RS}&settle=${args.stills ? 60 : Number(args.settle || 4)}`);
await page.goto(`http://127.0.0.1:${port}/scene.html?w=${W}&h=${H}&rs=${RS}&settle=${args.stills ? 60 : Number(args.settle || 4)}&fps=${FPS}`);
await page.waitForFunction(() => window.rpo?.ready, null, { timeout: 180000 });
const total = Math.round((await page.evaluate(() => window.rpo.duration)) * FPS);
if (args.stills) {
const dir = join(outDir, 'stills');
+32 -9
View File
@@ -84,16 +84,37 @@
#clock .shadow { display: none; margin-top: calc(14 * var(--u)); font: 700 calc(26 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.04em; color: #b9c8ff; }
/* Readout */
#readout { position: absolute; right: var(--pad); bottom: calc(250 * var(--u)); text-align: right; }
#readout { position: absolute; right: var(--pad); bottom: calc(190 * var(--u)); text-align: right; }
#readout .label { font: 800 calc(26 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.14em; color: var(--ink-2); }
#readout .val { font: 700 calc(132 * var(--u))/0.95 'JetBrains Mono', monospace; letter-spacing: -0.05em; margin-top: calc(8 * var(--u)); }
#readout .val small { font-size: 0.42em; color: var(--ink-2); margin-left: 0.12em; letter-spacing: 0; }
#readout .ctx { margin-top: calc(10 * var(--u)); font: 600 calc(32 * var(--u))/1.2 Inter, sans-serif; color: var(--ink); }
#mag { position: absolute; left: var(--pad); bottom: calc(276 * var(--u)); padding: calc(7 * var(--u)) calc(14 * var(--u)); border-radius: calc(6 * var(--u));
#mag { position: absolute; left: var(--pad); bottom: calc(196 * var(--u)); padding: calc(7 * var(--u)) calc(14 * var(--u)); border-radius: calc(6 * var(--u));
font: 800 calc(28 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.02em; color: #000; background: #fff; }
#mag.enlarged { background: var(--burn); }
.nw { white-space: nowrap; }
#clock .pills { display: flex; gap: calc(10 * var(--u)); justify-content: flex-end; margin-top: calc(16 * var(--u)); }
#clock .pills .rate { margin-top: 0; }
#clock .ff { display: none; padding: calc(10 * var(--u)) calc(14 * var(--u)); font: 800 calc(26 * var(--u))/1.15 Inter, sans-serif;
letter-spacing: 0.06em; text-transform: uppercase; color: #000; background: var(--burn); border-radius: calc(8 * var(--u)); }
#clock .light { display: inline-flex; align-items: center; gap: calc(10 * var(--u)); margin-top: calc(14 * var(--u));
font: 800 calc(26 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.04em; text-transform: uppercase; }
#clock .light i { width: calc(18 * var(--u)); height: calc(18 * var(--u)); border-radius: 50%; }
#clock .light.day { color: #ffe08a; } #clock .light.day i { background: #ffd34d; box-shadow: 0 0 calc(14 * var(--u)) #ffd34d; }
#clock .light.night { color: #b9c8ff; } #clock .light.night i { background: #20263a; border: calc(3 * var(--u)) solid #b9c8ff; }
#readout .rel { margin-top: calc(6 * var(--u)); font: 600 calc(28 * var(--u))/1.2 Inter, sans-serif; color: var(--ink-2); }
/* Timeline */
#tlwrap { position: absolute; left: var(--pad); right: var(--pad); bottom: calc(52 * var(--u)); }
#tlwrap .lab { font: 700 calc(24 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.1em; color: var(--ink-3); margin-bottom: calc(8 * var(--u)); }
#tlwrap .lab span { color: var(--burn); } #tlwrap .lab b { color: var(--ink-2); font-weight: 700; }
#timeline { height: calc(76 * var(--u)); }
#timeline svg { width: 100%; height: 100%; overflow: visible; font-family: 'JetBrains Mono', monospace; font-weight: 600; }
#interstitial { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; opacity: 0;
background: radial-gradient(ellipse at center, rgba(0,0,0,0.55), rgba(0,0,0,0.15) 70%); }
#interstitial .k { font: 800 calc(30 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.3em; text-transform: uppercase; color: var(--ink-2); }
#interstitial .d { margin-top: calc(18 * var(--u)); font: 700 calc(110 * var(--u))/1 'JetBrains Mono', monospace; letter-spacing: -0.03em; }
/* Chart */
#chartwrap { position: absolute; left: var(--pad); right: var(--pad); bottom: calc(58 * var(--u)); }
#chartwrap .lab { font: 700 calc(24 * var(--u))/1 Inter, sans-serif; letter-spacing: 0.12em; color: var(--ink-3); margin-bottom: calc(10 * var(--u)); }
@@ -138,11 +159,12 @@
body.portrait #caption.hook h1 { font-size: calc(76 * var(--u)); }
body.portrait #caption p, body.portrait #caption.hook p { font-size: calc(38 * var(--u)); max-width: none; }
body.portrait #events { top: calc(700 * var(--u)); width: auto; right: var(--pad); }
body.portrait #readout { right: auto; left: var(--pad); bottom: calc(690 * var(--u)); text-align: left; }
body.portrait #readout { right: auto; left: var(--pad); bottom: calc(580 * var(--u)); text-align: left; }
body.portrait #readout .val { font-size: calc(132 * var(--u)); }
body.portrait #mag { left: auto; right: var(--pad); bottom: calc(700 * var(--u)); }
body.portrait #chartwrap { bottom: calc(450 * var(--u)); }
body.portrait #chart { height: calc(170 * var(--u)); }
body.portrait #mag { left: auto; right: var(--pad); bottom: calc(590 * var(--u)); }
body.portrait #tlwrap { bottom: calc(440 * var(--u)); }
body.portrait #timeline { height: calc(80 * var(--u)); }
body.portrait #interstitial .d { font-size: calc(120 * var(--u)); }
body.portrait #topshade { height: calc(1000 * var(--u)); }
body.portrait #botshade { height: calc(1200 * var(--u)); background: linear-gradient(transparent, rgba(0,0,0,0.78) 45%, rgba(0,0,0,0.85)); }
body.portrait #credits { white-space: normal; bottom: calc(400 * var(--u)); font-size: calc(17 * var(--u)); }
@@ -166,10 +188,11 @@
<div id="chapters" class="shadow-text"></div>
<div id="caption" class="shadow-text"><h1></h1><p></p></div>
<div id="events" class="shadow-text"></div>
<div id="clock" class="shadow-text"><div class="date"></div><div class="time"></div><div class="rate"></div><div class="shadow">● IN EARTH'S SHADOW</div></div>
<div id="readout" class="shadow-text"><div class="label">DISTANCE APART</div><div class="val"></div><div class="ctx"></div></div>
<div id="clock" class="shadow-text"><div class="date"></div><div class="time"></div><div class="pills"><div class="ff">Fast-forward</div><div class="rate"></div></div><div class="light"></div></div>
<div id="readout" class="shadow-text"><div class="label">DISTANCE APART</div><div class="val"></div><div class="ctx"></div><div class="rel"></div></div>
<div id="mag" class="shadow-text"></div>
<div id="chartwrap"><div class="lab">DISTANCE OVER 22 DAYS · LOG SCALE</div><div id="chart"></div></div>
<div id="tlwrap"><div class="lab">22 DAYS · <span>● ENGINE BURN</span> · <b>○ CLOSEST PASS</b></div><div id="timeline"></div></div>
<div id="interstitial"><div class="k">22 days earlier</div><div class="d">SEP 30, 2026</div></div>
<div id="endcard"></div>
<div id="endlogo"><img src="logo.svg" alt=""><span>GOD'S EYE VIEW</span></div>
<div id="credits">Reconstruction, not tracking data · Orbits: CelesTrak, KeepTrack, LeoLabs, M. Langbroek · Imagery: Esri, Maxar, Earthstar Geographics, NASA · Sky: ESO/S. Brunier (CC BY 4.0), Yale BSC</div>
+211 -172
View File
@@ -4,6 +4,7 @@
// renders, and resolves once imagery and models have settled.
import { createHud } from './hud.js';
import { buildStory } from './story.js';
const params = new URLSearchParams(location.search);
const W = Number(params.get('w') || 1920);
@@ -18,7 +19,8 @@ const T0 = Date.parse(D.t0);
const DT = D.dt;
const NS = D.sep_km.length;
const sec = (iso) => (Date.parse(iso) - T0) / 1000;
const DUR = 45;
const FPS = Number(params.get('fps') || 30);
const SHUTTER = 0.5; // 180° shutter for motion blur
const SETTLE = Number(params.get('settle') || 4);
// ---------------------------------------------------------------------------
@@ -72,7 +74,7 @@ function basis(r, v) {
// Curvilinear placement: along-track offset is an arc on the anchor orbit.
function place(anchor, B, rel) {
const rm = v3.magnitude(anchor);
const th = rel[1] / rm;
const th = rel[1] / (D.a_ref_km * 1000);
const c = Math.cos(th);
const s = Math.sin(th);
const Rp = new v3(B.R.x * c + B.T.x * s, B.R.y * c + B.T.y * s, B.R.z * c + B.T.z * s);
@@ -86,24 +88,6 @@ function place(anchor, B, rel) {
// ---------------------------------------------------------------------------
const CA_T = sec(D.ca.t);
const VANTOR_T = sec(D.vantor.t);
const TAU_KEYS = [
[0.0, '2026-09-30T07:30:00Z'],
[4.0, '2026-09-30T10:20:00Z'],
[7.0, '2026-09-30T13:00:00Z'],
[12.5, '2026-10-11T23:20:00Z'],
[15.0, '2026-10-12T03:20:00Z'],
[17.0, '2026-10-15T01:30:00Z'],
[18.6, '2026-10-15T04:40:00Z'],
[20.6, '2026-10-16T04:40:00Z'],
[22.2, '2026-10-16T07:40:00Z'],
[25.8, '2026-10-17T15:40:00Z'],
[27.6, '2026-10-17T18:10:00Z'],
[29.8, D.ca.t, -14 * 60],
[35.0, D.ca.t, 11 * 60],
[36.4, '2026-10-18T07:00:00Z'],
[40.6, '2026-10-21T10:00:00Z'],
[45.0, '2026-10-21T13:00:00Z'],
].map(([s, iso, off = 0]) => [s, sec(iso) + off]);
function monotone(keys) {
const n = keys.length;
@@ -125,50 +109,57 @@ function monotone(keys) {
m[i + 1] = t * b * d[i];
}
}
const f = (s) => {
return (s) => {
let i = 0;
while (i < n - 2 && s > xs[i + 1]) i++;
const hh = xs[i + 1] - xs[i];
const t = Math.min(Math.max((s - xs[i]) / hh, 0), 1);
const t2 = t * t;
const t3 = t2 * t;
return (
(2 * t3 - 3 * t2 + 1) * ys[i] + (t3 - 2 * t2 + t) * hh * m[i] + (-2 * t3 + 3 * t2) * ys[i + 1] + (t3 - t2) * hh * m[i + 1]
);
return (2 * t3 - 3 * t2 + 1) * ys[i] + (t3 - 2 * t2 + t) * hh * m[i] + (-2 * t3 + 3 * t2) * ys[i + 1] + (t3 - t2) * hh * m[i + 1];
};
return f;
}
const tau = monotone(TAU_KEYS);
const rate = (s) => (tau(s + 0.01) - tau(s - 0.01)) / 0.02;
const S_SYNC0 = 29.8;
const S_SYNC1 = 35.0;
function hermite(s, s0, y0, m0, s1, y1, m1) {
const h = s1 - s0;
const t = (s - s0) / h;
const t2 = t * t;
const t3 = t2 * t;
return (2 * t3 - 3 * t2 + 1) * y0 + (t3 - 2 * t2 + t) * h * m0 + (-2 * t3 + 3 * t2) * y1 + (t3 - t2) * h * m1;
// Shots own their clocks (see story.js). tau = simulation time; bclock = the
// clock the Earth, sun and X36's place on its orbit follow (== tau in `true` shots).
const STORY = buildStory({ sec, D });
const SHOTS = STORY.shots.map((sh) => ({ ...sh, tauF: monotone(sh.tau) }));
const DUR = STORY.duration;
const CUTS = SHOTS.slice(1).map((sh) => sh.s0);
function shotAt(s) {
let k = 0;
for (let i = 0; i < SHOTS.length; i++) if (s >= SHOTS[i].s0) k = i;
return SHOTS[k];
}
const tau = (s) => shotAt(s).tauF(s);
function rate(s) {
const sh = shotAt(s);
const a = Math.max(sh.s0, s - 0.02);
const b = Math.min(sh.s1 - 1e-4, s + 0.02);
return (sh.tauF(b) - sh.tauF(a)) / (b - a);
}
const BG_RATE = 110;
function bclock(s) {
if (s >= S_SYNC0 && s <= S_SYNC1) return tau(s);
if (s < S_SYNC0) {
const y1 = tau(S_SYNC0);
return hermite(s, 0, y1 - S_SYNC0 * BG_RATE * 1.15, BG_RATE, S_SYNC0, y1, rate(S_SYNC0));
}
const y0 = tau(S_SYNC1);
return hermite(s, S_SYNC1, y0, rate(S_SYNC1), DUR, y0 + (DUR - S_SYNC1) * BG_RATE * 1.2, BG_RATE);
const sh = shotAt(s);
return sh.mode === 'true' ? sh.tauF(s) : sh.tauF(sh.s0) + (s - sh.s0) * (sh.bgRate || 140);
}
// Video time at which simulation time t is shown (skipping the cold open).
function sOfTau(t) {
let lo = 0;
let hi = DUR;
for (let k = 0; k < 50; k++) {
const mid = (lo + hi) / 2;
if (tau(mid) < t) lo = mid;
else hi = mid;
for (let i = SHOTS.length - 1; i >= 1; i--) {
const sh = SHOTS[i];
const a = sh.tauF(sh.s0);
const b = sh.tauF(sh.s1);
if (t >= a && t <= b) {
let lo = sh.s0;
let hi = sh.s1;
for (let k = 0; k < 40; k++) {
const mid = (lo + hi) / 2;
if (sh.tauF(mid) < t) lo = mid;
else hi = mid;
}
return lo;
}
}
return lo;
return null;
}
// ---------------------------------------------------------------------------
@@ -176,64 +167,37 @@ function sOfTau(t) {
// ---------------------------------------------------------------------------
const BURNS = D.burns.map((b) => ({ ...b, t: sec(b.t) }));
// Camera keys, in X36's local frame (R up, T velocity, N orbit normal).
// dist: metres (or auto = f * separation, floored at min). az: 0 = behind X36
// looking forward. look: 0 = X36, 1 = Cosmos. earth: pull aim to Earth centre.
const WIDE = { f: 0, look: 0, earth: 0.86, fov: 40, mag: 0, sun: 0.55 };
const CAM = [
{ s: 0.0, ...WIDE, dist: 5.4e7, az: 82, el: 36, trail: 2 },
{ s: 6.0, ...WIDE, dist: 4.6e7, az: 96, el: 34, trail: 20 },
{ s: 12.5, ...WIDE, dist: 4.4e7, az: 118, el: 30, trail: 30 },
{ s: 12.5, cut: true, dist: 6.0e5, f: 2.8, az: 50, el: 38, look: 0.5, earth: 0, fov: 34, mag: 0, trail: 3 },
{ s: 15.4, dist: 1.6e6, f: 2.8, az: 32, el: 36, look: 0.5, earth: 0, fov: 34, mag: 0, trail: 8 },
{ s: 18.0, dist: 1.4e6, f: 2.8, az: 26, el: 34, look: 0.5, earth: 0, fov: 34, mag: 0, trail: 12 },
{ s: 20.4, dist: 4.0e4, f: 2.8, az: 40, el: 32, look: 0.5, earth: 0, fov: 34, mag: 0, trail: 10 },
{ s: 22.4, dist: 6.5e3, f: 2.4, az: 58, el: 34, look: 0.45, earth: 0, fov: 36, mag: 1, mx: 40, trail: 9 },
{ s: 26.0, dist: 4.6e3, f: 2.4, az: 76, el: 30, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 40, trail: 9 },
{ s: 29.7, dist: 2.0e3, f: 2.2, az: 104, el: 24, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 4 },
{ s: 31.9, dist: 1.0e3, f: 2.2, az: 116, el: 22, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 3 },
{ s: 31.9, cut: true, dist: 26, f: 0, az: 160, el: 0, look: 0.0, earth: 0, fov: 34, mag: 0, trail: 0.5, comp: 0, pairD: 26, pairTh: 22 },
{ s: 33.9, dist: 22, f: 0, az: 163, el: 0, look: 0.0, earth: 0, fov: 34, mag: 0, trail: 0.5, comp: 0, pairD: 22, pairTh: 18 },
{ s: 33.9, cut: true, dist: 1.3e3, f: 2.2, az: 96, el: 18, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 1.6 },
{ s: 35.6, dist: 1.6e3, f: 2.4, az: 100, el: 22, look: 0.5, earth: 0, fov: 36, mag: 1, mx: 25, trail: 2 },
{ s: 37.0, dist: 1.2e4, f: 2.8, az: 110, el: 30, look: 0.4, earth: 0, fov: 36, mag: 1, mx: 40, trail: 4 },
{ s: 39.0, dist: 9.0e5, f: 2.8, az: 60, el: 30, look: 0.5, earth: 0, fov: 34, mag: 0, trail: 30 },
{ s: 41.2, dist: 2.0e6, f: 2.8, az: 44, el: 30, look: 0.5, earth: 0, fov: 34, mag: 0, trail: 60 },
{ s: 41.2, cut: true, ...WIDE, dist: 4.8e7, az: 80, el: 34, trail: 60 },
{ s: 45.0, ...WIDE, dist: 4.2e7, az: 96, el: 32, trail: 60 },
];
const CUTS = CAM.filter((k) => k.cut).map((k) => k.s);
const smooth = (x) => x * x * x * (x * (x * 6 - 15) + 10);
function camAt(s) {
// Keys are grouped into shots at cut keys; interpolation never crosses a cut.
let start = 0;
for (let k = 1; k < CAM.length; k++) if (CAM[k].cut && s >= CAM[k].s) start = k;
let end = CAM.length - 1;
for (let k = start + 1; k < CAM.length; k++) if (CAM[k].cut) { end = k - 1; break; }
let i = start;
while (i < end - 1 && s > CAM[i + 1].s) i++;
const a = CAM[i];
const b = CAM[Math.min(i + 1, end)];
const keys = shotAt(s).cam;
let i = 0;
while (i < keys.length - 2 && s > keys[i + 1].s) i++;
const a = keys[i];
const b = keys[Math.min(i + 1, keys.length - 1)];
const u = b === a ? 0 : smooth(Math.min(Math.max((s - a.s) / (b.s - a.s), 0), 1));
const lerp = (x, y) => x + (y - x) * u;
const lg = (x, y) => Math.exp(lerp(Math.log(x), Math.log(y)));
const ar = a.aimRel || [0, 0, 0];
const br = b.aimRel || [0, 0, 0];
return {
dist: Math.exp(lerp(Math.log(a.dist), Math.log(b.dist))),
f: lerp(a.f, b.f),
frame: a.frame || 'rtn',
dist: lg(a.dist, b.dist),
f: lerp(a.f || 0, b.f || 0),
az: lerp(a.az, b.az),
el: lerp(a.el, b.el),
look: lerp(a.look, b.look),
earth: lerp(a.earth, b.earth),
look: lerp(a.look || 0, b.look || 0),
earth: lerp(a.earth || 0, b.earth || 0),
fov: lerp(a.fov, b.fov),
mag: lerp(a.mag, b.mag),
mag: lerp(a.mag || 0, b.mag || 0),
mx: lg(a.mx || 1, b.mx || 1),
rtrail: lerp(a.rtrail || 0, b.rtrail || 0),
atrail: lerp(a.atrail || 0, b.atrail || 0),
sun: lerp(a.sun || 0, b.sun || 0),
vup: lerp(a.vup || 0, b.vup || 0),
comp: lerp(a.comp ?? 1, b.comp ?? 1),
pairD: lerp(a.pairD || 0, b.pairD || 0),
pairTh: lerp(a.pairTh || 0, b.pairTh || 0),
mx: Math.exp(lerp(Math.log(a.mx || 1), Math.log(b.mx || 1))),
trail: Math.exp(lerp(Math.log(a.trail), Math.log(b.trail))),
aimRel: [0, 1, 2].map((k) => lerp(ar[k], br[k])),
sun: lerp(a.sun || 0, b.sun || 0),
};
}
@@ -253,6 +217,13 @@ const viewer = new Cesium.Viewer('globe', {
msaaSamples: 1,
});
const scene = viewer.scene;
// Motion-blur accumulator: sub-frames are averaged here, above the WebGL canvas.
const accum = document.createElement('canvas');
accum.width = W;
accum.height = H;
accum.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;visibility:hidden';
document.getElementById('globe').appendChild(accum);
const actx = accum.getContext('2d');
// Night side: NASA Black Marble city lights (keyless GIBS tiles).
const nightLayer = viewer.imageryLayers.addImageryProvider(new Cesium.UrlTemplateImageryProvider({
url: 'https://gibs.earthdata.nasa.gov/wmts/epsg3857/best/VIIRS_Black_Marble/default/2016-01-01/GoogleMapsCompatible_Level8/{z}/{y}/{x}.png',
@@ -385,7 +356,7 @@ function project(world) {
return p;
}
const hud = createHud({
D, W, H, U, PORTRAIT, sec, rate, sOfTau, sepAt, CA_T, VANTOR_T, BURNS, CUTS, project,
D, W, H, U, PORTRAIT, sec, rate, sOfTau, sepAt, CA_T, VANTOR_T, BURNS, CUTS, SHOTS, shotAt, DUR, project,
});
// ---------------------------------------------------------------------------
@@ -416,14 +387,24 @@ function eclipseLight(worldPos, sunW) {
return Math.min(1, Math.max(0, 0.5 + e));
}
async function frame(s) {
// Orbital speed from vis-viva, using each satellite's own SMA (m/s).
const MU = 3.986004418e14;
function smaOffsetAt(t) {
const arr = D.sma_offset_km;
const x = Math.min(Math.max(t / (DT * 10), 0), arr.length - 1);
return arr[Math.round(x)] * 1000;
}
// Set every scene element for video time s. Returns state for the HUD.
function setState(s) {
const sh = shotAt(s);
const t = tau(s);
const b = bclock(s);
const jd = Cesium.JulianDate.addSeconds(Cesium.JulianDate.fromIso8601(D.t0), b, new Cesium.JulianDate());
Cesium.Transforms.computeTemeToPseudoFixedMatrix(jd, M);
const cam = camAt(s);
// Anchor (X36 at backdrop time) and relative state at simulation time.
// Anchor (X36 on the shot's display clock) and relative state at simulation time.
const A = x36Teme(b);
const B = basis(A.r, A.v);
const rel = relAt(t);
@@ -433,25 +414,51 @@ async function frame(s) {
const TW = toWorld(B.T);
const NW = toWorld(B.N);
const sep = v3.distance(x36W, cosW);
// Inertial camera basis: X36's frame frozen at the shot start.
let cR = RW;
let cT = TW;
let cN = NW;
if (cam.frame === 'inertial') {
const A0 = x36Teme(bclock(sh.s0));
const B0 = basis(A0.r, A0.v);
cR = toWorld(B0.R);
cT = toWorld(B0.T);
cN = toWorld(B0.N);
}
// Camera
const sunW = v3.normalize(Cesium.Simon1994PlanetaryPositions.computeSunPositionInEarthInertialFrame(jd, new v3()), new v3());
Cesium.Matrix3.multiplyByVector(Cesium.Transforms.computeIcrfToFixedMatrix(jd) || M, sunW, sunW);
v3.normalize(sunW, sunW);
// Camera orbits a pivot on the X36 -> Cosmos line; aimRel only tilts the gaze.
const pivot = v3.lerp(x36W, cosW, cam.look, new v3());
// Frame on Cosmos's guiding centre (its loop averaged over one orbit) and the
// loop's size: both change slowly, so the shot doesn't pump every orbit.
const P = 5730;
const gc = [0, 0, 0];
const relS = [];
for (let k = 0; k < 16; k++) {
const rk = relAt(t - P / 2 + (k + 0.5) * (P / 16));
relS.push(rk);
gc[0] += rk[0] / 16;
gc[1] += rk[1] / 16;
gc[2] += rk[2] / 16;
}
let loop = 0;
for (const rk of relS) loop = Math.max(loop, Math.hypot(rk[0] - gc[0], rk[1] - gc[1], rk[2] - gc[2]));
const gcW = toWorld(place(A.r, B, gc));
const sepG = v3.distance(x36W, gcW) + loop;
const pivot = v3.lerp(x36W, gcW, cam.look, new v3());
v3.lerp(pivot, v3.ZERO, cam.earth, pivot);
const aim = v3.clone(pivot);
v3.add(aim, v3.multiplyByScalar(RW, cam.aimRel[0], new v3()), aim);
v3.add(aim, v3.multiplyByScalar(TW, cam.aimRel[1], new v3()), aim);
v3.add(aim, v3.multiplyByScalar(NW, cam.aimRel[2], new v3()), aim);
const dist = Math.max(cam.dist, cam.f * sep) * (PORTRAIT && cam.earth > 0.5 ? 0.74 : 1);
const dist = Math.max(cam.dist, cam.f * sepG) * (PORTRAIT && cam.earth > 0.5 ? 0.74 : 1);
const az = Cesium.Math.toRadians(cam.az);
const el = Cesium.Math.toRadians(cam.el);
const off = new v3();
v3.add(off, v3.multiplyByScalar(TW, -Math.cos(el) * Math.cos(az), new v3()), off);
v3.add(off, v3.multiplyByScalar(NW, Math.cos(el) * Math.sin(az), new v3()), off);
v3.add(off, v3.multiplyByScalar(RW, Math.sin(el), new v3()), off);
v3.add(off, v3.multiplyByScalar(cT, -Math.cos(el) * Math.cos(az), new v3()), off);
v3.add(off, v3.multiplyByScalar(cN, Math.cos(el) * Math.sin(az), new v3()), off);
v3.add(off, v3.multiplyByScalar(cR, Math.sin(el), new v3()), off);
if (cam.sun > 0) {
v3.normalize(v3.add(v3.multiplyByScalar(off, 1 - cam.sun, off), v3.multiplyByScalar(sunW, cam.sun, new v3()), off), off);
}
@@ -471,20 +478,23 @@ async function frame(s) {
const minR = 6.371e6 + 1.2e5;
if (v3.magnitude(camPos) < minR) v3.multiplyByScalar(v3.normalize(camPos, camPos), minR, camPos);
const dir = v3.normalize(v3.subtract(aim, camPos, new v3()), new v3());
let right = v3.normalize(v3.cross(dir, RW, new v3()), new v3());
// Up: local zenith; in portrait chase shots, the direction of flight (so the
// pair travels up the tall frame and Earth's limb runs down one side).
const vup = PORTRAIT ? cam.vup : 0;
const upHint = v3.normalize(v3.lerp(RW, TW, vup, new v3()), new v3());
let right = v3.normalize(v3.cross(dir, upHint, new v3()), new v3());
let up = v3.cross(right, dir, new v3());
{
// Composition: put the aim point right of centre in 16:9 (the caption
// column is on the left); portrait keeps it centred between the text bands.
// Composition: put the aim point right of centre in 16:9 (caption column on the left).
const fovR = Cesium.Math.toRadians(PORTRAIT ? cam.fov * 1.55 : cam.fov);
const aspect = W / H;
const tanH = PORTRAIT ? Math.tan(fovR / 2) * aspect : Math.tan(fovR / 2);
const tanV = PORTRAIT ? Math.tan(fovR / 2) : Math.tan(fovR / 2) / aspect;
const ndcX = (PORTRAIT ? 0 : 0.2) * cam.comp;
const ndcY = (PORTRAIT ? -0.02 : -0.06) * cam.comp;
const ndcX = (PORTRAIT ? 0 : 0.18) * cam.comp;
const ndcY = (PORTRAIT ? -0.04 : 0.06) * cam.comp;
v3.normalize(v3.add(v3.add(dir, v3.multiplyByScalar(right, -ndcX * tanH, new v3()), new v3()),
v3.multiplyByScalar(up, -ndcY * tanV, new v3()), new v3()), dir);
right = v3.normalize(v3.cross(dir, RW, new v3()), new v3());
right = v3.normalize(v3.cross(dir, upHint, new v3()), new v3());
up = v3.cross(right, dir, new v3());
}
scene.camera.setView({ destination: camPos, orientation: { direction: dir, up } });
@@ -499,22 +509,14 @@ async function frame(s) {
}
scene.camera.frustum.near = Math.max(0.2, Math.min(dist * 0.02, 1000));
// Models: attitude, magnification, lighting
// Models: attitude, fixed per-shot enlargement, lighting
const camDistX = v3.distance(camPos, x36W);
const camDistC = v3.distance(camPos, cosW);
const pxPerRad = (PORTRAIT ? W : W) / (2 * Math.tan(scene.camera.frustum.fov / 2));
const targetPx = 110 * U; // magnified apparent size
const magFor = (d, size) => Math.max(1, (targetPx * d) / (pxPerRad * size));
// One fixed enlargement per beat (keyed), so the on-screen factor holds still.
const rawMag = cam.mag > 0.5 ? cam.mx : 1;
const MAG_MAX = 150;
const mag = rawMag;
void magFor;
const nearScale = Math.min(1, Math.max(0, cam.mag * 2));
mX36.show = (cam.mag > 0.01 && rawMag <= MAG_MAX) || camDistX < 3000;
mCos.show = (cam.mag > 0.01 && rawMag <= MAG_MAX) || camDistC < 6000;
const pxPerRad = W / (2 * Math.tan(scene.camera.frustum.fov / 2));
const mag = cam.mag > 0.5 ? cam.mx : 1;
mX36.show = cam.mag > 0.01 || camDistX < 3000;
mCos.show = cam.mag > 0.01 || camDistC < 6000;
mX36.modelMatrix = modelMatrix(x36W, TW, RW, mag);
// Cosmos points its sensor at X36 when close, otherwise flies prograde.
const toX = v3.normalize(v3.subtract(x36W, cosW, new v3()), new v3());
const w = Math.min(1, Math.max(0, (60e3 - sep) / 40e3));
const cx = v3.normalize(v3.lerp(TW, toX, w, new v3()), new v3());
@@ -522,69 +524,83 @@ async function frame(s) {
mCos.modelMatrix = modelMatrix(cosW, cx, cy, mag);
const lx = eclipseLight(x36W, sunW);
const lc = eclipseLight(cosW, sunW);
// In Earth's shadow a dim blue fill keeps the shapes legible.
const lightFor = (l) => new v3(0.6 + (2.3 - 0.6) * l, 0.66 + (2.3 - 0.66) * l, 0.92 + (2.3 - 0.92) * l);
mX36.lightColor = lightFor(lx);
mCos.lightColor = lightFor(lc);
// Glows: shown when the model is too small to read.
// Glows: always on as "lights"; they shrink when the model is big enough to read.
const pxX = (3.25 * mag * pxPerRad) / camDistX;
const pxC = (3.6 * mag * pxPerRad) / camDistC;
bX36.position = x36W;
bCos.position = cosW;
bX36.scale = (pxX > 24 ? 0.14 : 0.42) * U;
bCos.scale = (pxC > 24 ? 0.14 : 0.42) * U;
bX36.color = Cesium.Color.WHITE.withAlpha(pxX > 40 ? 0 : 1);
bCos.color = Cesium.Color.WHITE.withAlpha(pxC > 40 ? 0 : 1);
bX36.scale = (pxX > 24 ? 0.16 : 0.5) * U;
bCos.scale = (pxC > 24 ? 0.16 : 0.5) * U;
bX36.color = Cesium.Color.WHITE.withAlpha(pxX > 60 ? 0.35 : 1);
bCos.color = Cesium.Color.WHITE.withAlpha(pxC > 60 ? 0.35 : 1);
// Burn flare
let flare = 0;
for (const bn of BURNS) {
const sb = sOfTauCached(bn.t);
if (sb === null) continue;
const k = (s - sb) / 0.9;
if (k > -0.08 && k < 1) flare = Math.max(flare, k < 0 ? 1 + k / 0.08 : (1 - k) ** 1.6);
}
bBurn.position = cosW;
bBurn.scale = flare * 0.9 * U;
bBurn.scale = flare * 1.1 * U;
bBurn.color = Cesium.Color.WHITE.withAlpha(flare);
// Trails and reference geometry
// Trails
clearDynamic();
// X36 orbit ring (inertial, at the anchor epoch); fades out up close.
const ringA = Math.min(1, Math.max(0, (camDistX - 1.5e4) / 6e4));
if (ringA > 0.01) {
const P = 5730;
const pts = [];
const cols = [];
const n = 240;
for (let k = 0; k <= n; k++) {
const tt = b - P + (k / n) * P;
pts.push(toWorld(x36Teme(tt).r));
const a = 0.06 + 0.5 * (k / n) ** 3;
cols.push(COL_X36.withAlpha(a * ringA));
// Orbit trails (inertial): where each satellite has actually flown in the last
// `atrail` minutes. Only meaningful when the shot runs on one clock.
if (cam.atrail > 0 && sh.mode === 'true') {
const span = cam.atrail * 60;
const n = Math.min(400, Math.max(40, Math.round(span / 15)));
for (const which of ['x36', 'cos']) {
const pts = [];
const cols = [];
const col = which === 'x36' ? COL_X36 : COL_COS;
for (let k = 0; k <= n; k++) {
const tt = t - span + (k / n) * span;
if (tt < 0) continue;
const Ak = x36Teme(tt);
const p = which === 'x36' ? Ak.r : place(Ak.r, basis(Ak.r, Ak.v), relAt(tt));
pts.push(toWorld(p));
cols.push(col.withAlpha(0.02 + 0.8 * (k / n) ** 2.2));
}
pts.push(which === 'x36' ? x36W : cosW);
cols.push(col.withAlpha(0.85));
polyline(pts, cols, which === 'x36' ? 3 : 3);
}
polyline(pts, cols, 1.6);
}
// Cosmos relative trail, expressed in the current anchor frame
{
const span = cam.trail * 3600;
const n = Math.min(900, Math.max(60, Math.round(span / 45)));
// Cosmos relative trail (its path as seen from X36), in the current anchor frame
if (cam.rtrail > 0) {
const span = cam.rtrail * 3600;
const n = Math.min(1200, Math.max(60, Math.round(span / 40)));
const pts = [];
const cols = [];
for (let k = 0; k <= n; k++) {
const tt = t - span + (k / n) * span;
if (tt < 0) continue;
pts.push(toWorld(place(A.r, B, relAt(tt))));
cols.push(COL_COS.withAlpha(0.04 + 0.92 * (k / n) ** 1.8));
cols.push(COL_COS.withAlpha(0.05 + 0.9 * (k / n) ** 1.6));
}
pts.push(cosW);
cols.push(COL_COS.withAlpha(1));
polyline(pts, cols, 2.4);
polyline(pts, cols, 2.6);
}
// Separation line when the pair is within view range
if (sep < 900e3) {
const a = Math.min(1, (900e3 - sep) / 200e3) * 0.55;
polyline([x36W, cosW], [Cesium.Color.WHITE.withAlpha(a * 0.25), Cesium.Color.WHITE.withAlpha(a * 0.25)], 1);
// X36 orbit ring for wide shots (inertial, at the anchor epoch)
const ringA = Math.min(1, Math.max(0, (camDistX - 1.5e4) / 6e4)) * (cam.atrail > 0 && sh.mode === 'true' ? 0.35 : 1);
if (ringA > 0.01) {
const P = 5730;
const pts = [];
const cols = [];
for (let k = 0; k <= 240; k++) {
pts.push(toWorld(x36Teme(b - P + (k / 240) * P).r));
cols.push(COL_X36.withAlpha((0.05 + 0.3 * (k / 240) ** 2) * ringA));
}
polyline(pts, cols, 1.4);
}
// RIC reference rings (orbital plane) during proximity
const gridA = Math.min(1, Math.max(0, (8e3 - Math.max(sep, camDistX)) / 3e3)) * (cam.mag > 0.4 ? 1 : 0.35);
@@ -597,31 +613,54 @@ async function frame(s) {
const p = v3.add(x36W, v3.multiplyByScalar(RW, r * Math.sin(a), new v3()), new v3());
v3.add(p, v3.multiplyByScalar(TW, r * Math.cos(a), new v3()), p);
pts.push(p);
cols.push(Cesium.Color.WHITE.withAlpha(0.16 * gridA));
cols.push(Cesium.Color.WHITE.withAlpha(0.14 * gridA));
}
polyline(pts, cols, 1);
}
const ax = (vec, len, col) => polyline(
[v3.add(x36W, v3.multiplyByScalar(vec, -len, new v3()), new v3()), v3.add(x36W, v3.multiplyByScalar(vec, len, new v3()), new v3())],
[col.withAlpha(0), col.withAlpha(0)], 1,
);
polyline([x36W, v3.add(x36W, v3.multiplyByScalar(TW, 2600, new v3()), new v3())], [COL_X36.withAlpha(0.5 * gridA), COL_X36.withAlpha(0)], 1.5);
polyline([x36W, v3.add(x36W, v3.multiplyByScalar(RW, 1400, new v3()), new v3())], [Cesium.Color.WHITE.withAlpha(0.4 * gridA), Cesium.Color.WHITE.withAlpha(0)], 1.5);
void ax;
}
hud.update(s, t, b, { x36W, cosW, RW, TW, NW, rel, sep, mag, gridA, lx, camDistX, jd });
// Render; re-render only while imagery is still streaming in.
scene.render(jd);
// Bounded: parents stand in for children still streaming; sequential frames converge.
for (let k = 0; k < SETTLE && !(globe.tilesLoaded && mX36.ready && mCos.ready); k++) {
await new Promise((r) => setTimeout(r, 60));
scene.render(jd);
// Speeds: X36 from its state vector; Cosmos by vis-viva with its own SMA.
const rC = v3.magnitude(place(A.r, B, rel));
const aC = D.a_km * 1000 + smaOffsetAt(t);
const vC = Math.sqrt(MU * (2 / rC - 1 / aC));
const vXv = Math.sqrt(MU * (2 / v3.magnitude(A.r) - 1 / (D.a_km * 1000)));
const r1 = relAt(t + 30);
const r0 = relAt(t - 30);
const relSpeed = Math.hypot(r1[0] - r0[0], r1[1] - r0[1], r1[2] - r0[2]) / 60;
return { sh, t, b, jd, x36W, cosW, RW, TW, NW, rel, sep, mag, gridA, lx, camDistX, vX: vXv, vC, relSpeed };
}
async function frame(s) {
const sh = shotAt(s);
const K = Math.max(1, (sh.blur | 0) % 2 ? sh.blur | 0 : (sh.blur | 0) + 1);
globe.maximumScreenSpaceError = K > 1 ? 4 : 2.2;
const mid = Math.floor(K / 2);
let hudState = null;
accum.style.visibility = K > 1 ? 'visible' : 'hidden';
for (let k = 0; k < K; k++) {
// Sub-frames spread across the shutter, clamped inside the shot.
const sk = K === 1 ? s : Math.min(sh.s1 - 1e-4, Math.max(sh.s0, s + ((k + 0.5) / K - 0.5) * (SHUTTER / FPS)));
const st = setState(k === mid ? s : sk);
scene.render(st.jd);
if (k === 0 || K === 1) {
for (let j = 0; j < SETTLE && !(globe.tilesLoaded && mX36.ready && mCos.ready); j++) {
await new Promise((r) => setTimeout(r, 60));
scene.render(st.jd);
}
}
if (k === mid) {
hud.update(s, st.t, st.b, st);
hudState = st;
}
if (K > 1) {
actx.globalAlpha = 1 / (k + 1);
actx.drawImage(viewer.canvas, 0, 0, W, H);
}
}
const gl = scene.context._gl;
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
const dbg = { pX: Cesium.SceneTransforms.worldToWindowCoordinates(scene, x36W), dX: v3.distance(scene.camera.positionWC, x36W), ang: Cesium.Math.toDegrees(v3.angleBetween(scene.camera.directionWC, v3.subtract(x36W, scene.camera.positionWC, new v3()))), mshow: mX36.show, mm: Cesium.Matrix4.getTranslation(mX36.modelMatrix, new v3()), x36W };
return { dbg, s, t, b, sep, mag, camR: v3.magnitude(camPos), dist, fov: scene.camera.frustum.fov, fovy: scene.camera.frustum.fovy, aspect: scene.camera.frustum.aspectRatio };
return { s, t: hudState.t, sep: hudState.sep, mag: hudState.mag, shot: sh.id, K };
}
const sOfTauMemo = new Map();
@@ -631,4 +670,4 @@ function sOfTauCached(t) {
}
await document.fonts.ready;
window.rpo = { frame, tau, bclock, duration: DUR, ready: true, CAM };
window.rpo = { frame, tau, bclock, duration: DUR, ready: true, SHOTS };
+123
View File
@@ -0,0 +1,123 @@
// v4 storyboard: a list of shots separated by hard cuts. Each shot owns its
// clock. `true` shots run Earth, sun, both satellites and the camera on one
// physical clock (a real time-lapse: day and night cycle, the ground rushes
// by). `ff` (fast-forward) shots are too fast for that to read, so the Earth
// and X36's place on its orbit advance on a slow display clock while the
// relative motion runs at the stated rate; they carry a FAST-FORWARD badge.
//
// Camera keys use X36's local frame (R up, T velocity, N orbit normal):
// dist (m) or f × separation; az 0 = behind X36 looking forward; el up;
// look 0 = X36 … 1 = Cosmos; earth pulls the aim toward Earth's centre;
// frame 'inertial' freezes the basis at the shot start (wide shots);
// vup (portrait) rolls the camera so the direction of flight points up;
// rtrail = Cosmos relative-path trail (hours); atrail = orbit trails (min);
// mag/mx = fixed model enlargement; pairD/pairTh = pair-locked true-size cam.
export function buildStory({ sec, D }) {
const CA = sec(D.ca.t);
const M = 60;
const iso = (s) => sec(s);
const WIDE = { frame: 'inertial', look: 0, earth: 0.86, f: 0, fov: 40, mag: 0, sun: 0.5 };
const shots = [
{
id: 'hook', mode: 'true', s0: 0, s1: 3.6, blur: 2,
tau: [[0, CA - 7 * M], [3.6, CA - 2 * M]],
cam: [
{ s: 0, dist: 1500, f: 2.6, az: 118, el: 18, look: 0.5, fov: 34, mag: 1, mx: 25, rtrail: 1.2, atrail: 4, vup: 1 },
{ s: 3.6, dist: 1300, f: 2.4, az: 104, el: 14, look: 0.5, fov: 34, mag: 1, mx: 25, rtrail: 1.2, atrail: 4, vup: 1 },
],
},
{
id: 'climb', mode: 'true', s0: 3.6, s1: 10.6, blur: 3,
tau: [[3.6, iso('2026-09-30T07:20:00Z')], [10.6, iso('2026-09-30T12:40:00Z')]],
cam: [
{ s: 3.6, ...WIDE, dist: 3.1e7, az: 86, el: 38, atrail: 70, rtrail: 0 },
{ s: 10.6, ...WIDE, dist: 2.6e7, az: 104, el: 34, atrail: 70, rtrail: 0 },
],
},
{
id: 'lap', mode: 'ff', s0: 10.6, s1: 14.6, blur: 1, bgRate: 260,
tau: [[10.6, iso('2026-09-30T13:00:00Z')], [14.6, iso('2026-10-11T23:40:00Z')]],
cam: [
{ s: 10.6, ...WIDE, dist: 2.7e7, az: 100, el: 36, atrail: 0, rtrail: 30 },
{ s: 14.6, ...WIDE, dist: 2.3e7, az: 124, el: 30, atrail: 0, rtrail: 30 },
],
},
{
id: 'drop', mode: 'true', s0: 14.6, s1: 19.6, blur: 4,
tau: [[14.6, iso('2026-10-12T00:00:00Z')], [19.6, iso('2026-10-12T03:30:00Z')]],
cam: [
{ s: 14.6, dist: 2.4e5, f: 2.6, az: 62, el: 28, look: 0.5, fov: 36, mag: 0, rtrail: 3, atrail: 20, vup: 1 },
{ s: 17.0, dist: 3.0e5, f: 2.6, az: 48, el: 30, look: 0.5, fov: 36, mag: 0, rtrail: 4, atrail: 20, vup: 1 },
{ s: 19.6, dist: 4.0e5, f: 2.6, az: 34, el: 32, look: 0.5, fov: 36, mag: 0, rtrail: 4, atrail: 20, vup: 1 },
],
},
{
id: 'close', mode: 'ff', s0: 19.6, s1: 23.2, blur: 1, bgRate: 140,
tau: [[19.6, iso('2026-10-12T03:40:00Z')], [21.4, iso('2026-10-15T06:00:00Z')], [23.2, iso('2026-10-16T07:00:00Z')]],
cam: [
{ s: 19.6, dist: 1.0e6, f: 2.8, az: 30, el: 34, look: 0.5, fov: 34, mag: 0, rtrail: 30, atrail: 0, vup: 1 },
{ s: 23.2, dist: 2.0e4, f: 2.8, az: 46, el: 34, look: 0.5, fov: 34, mag: 0, rtrail: 30, atrail: 0, vup: 1 },
],
},
{
id: 'dance', mode: 'true', s0: 23.2, s1: 29.2, blur: 5,
tau: [[23.2, iso('2026-10-16T07:10:00Z')], [29.2, iso('2026-10-16T12:10:00Z')]],
cam: [
{ s: 23.2, dist: 5.5e3, f: 1.7, az: 62, el: 30, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 3, atrail: 1.2, vup: 1 },
{ s: 29.2, dist: 5.0e3, f: 1.7, az: 84, el: 24, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 3, atrail: 1.2, vup: 1 },
],
},
{
id: 'dance-ff', mode: 'ff', s0: 29.2, s1: 31.2, blur: 1, bgRate: 140,
tau: [[29.2, iso('2026-10-16T12:20:00Z')], [31.2, iso('2026-10-17T22:20:00Z')]],
cam: [
{ s: 29.2, dist: 4.5e3, f: 1.7, az: 70, el: 40, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 12, atrail: 0, vup: 1 },
{ s: 31.2, dist: 3.4e3, f: 1.7, az: 92, el: 34, look: 0.5, fov: 38, mag: 1, mx: 40, rtrail: 12, atrail: 0, vup: 1 },
],
},
{
id: 'approach', mode: 'true', s0: 31.2, s1: 35.4, blur: 4,
tau: [[31.2, iso('2026-10-17T22:30:00Z')], [33.4, CA - 40 * M], [35.4, CA - 4 * M]],
cam: [
{ s: 31.2, dist: 3.0e3, f: 1.7, az: 96, el: 26, look: 0.5, fov: 38, mag: 1, mx: 30, rtrail: 3, atrail: 1.5, vup: 1 },
{ s: 35.4, dist: 1.2e3, f: 2.2, az: 112, el: 18, look: 0.5, fov: 36, mag: 1, mx: 25, rtrail: 1.6, atrail: 1.5, vup: 1 },
],
},
{
id: 'ca', mode: 'true', s0: 35.4, s1: 37.4, blur: 2,
tau: [[35.4, CA - 4 * M], [37.4, CA + 1.5 * M]],
cam: [
{ s: 35.4, dist: 26, az: 160, el: 0, look: 0, fov: 34, mag: 0, rtrail: 0.4, atrail: 0.6, comp: 0, pairD: 26, pairTh: 22 },
{ s: 37.4, dist: 22, az: 163, el: 0, look: 0, fov: 34, mag: 0, rtrail: 0.4, atrail: 0.6, comp: 0, pairD: 22, pairTh: 18 },
],
},
{
id: 'sunrise', mode: 'true', s0: 37.4, s1: 40.0, blur: 2,
tau: [[37.4, CA + 2 * M], [40.0, CA + 27 * M]],
cam: [
{ s: 37.4, dist: 1.3e3, f: 2.2, az: 96, el: 16, look: 0.5, fov: 36, mag: 1, mx: 25, rtrail: 1.6, atrail: 1.5, vup: 1 },
{ s: 40.0, dist: 2.2e3, f: 2.4, az: 104, el: 22, look: 0.5, fov: 36, mag: 1, mx: 25, rtrail: 2, atrail: 1.5, vup: 1 },
],
},
{
id: 'leave', mode: 'ff', s0: 40.0, s1: 44.0, blur: 1, bgRate: 140,
tau: [[40.0, CA + 30 * M], [42.0, iso('2026-10-19T08:00:00Z')], [44.0, iso('2026-10-21T12:00:00Z')]],
cam: [
{ s: 40.0, dist: 1.2e4, f: 2.8, az: 110, el: 30, look: 0.4, fov: 36, mag: 1, mx: 40, rtrail: 8, atrail: 0, vup: 1 },
{ s: 42.0, dist: 4.0e5, f: 2.8, az: 70, el: 30, look: 0.5, fov: 34, mag: 0, rtrail: 30, atrail: 0, vup: 1 },
{ s: 44.0, dist: 1.6e6, f: 2.8, az: 44, el: 30, look: 0.5, fov: 34, mag: 0, rtrail: 60, atrail: 0, vup: 1 },
],
},
{
id: 'end', mode: 'true', s0: 44.0, s1: 48.5, blur: 1,
tau: [[44.0, iso('2026-10-21T12:00:00Z')], [48.5, iso('2026-10-21T14:00:00Z')]],
cam: [
{ s: 44.0, ...WIDE, dist: 4.6e7, az: 82, el: 34, atrail: 60, rtrail: 0 },
{ s: 48.5, ...WIDE, dist: 4.2e7, az: 96, el: 32, atrail: 60, rtrail: 0 },
],
},
];
return { shots, duration: 48.5 };
}
+195
View File
@@ -0,0 +1,195 @@
ICEYE-X36
1 59103U 24043F 26086.28993682 .00004707 00000+0 32592-3 0 9991
2 59103 97.8231 223.0188 0005114 225.4450 134.6360 15.06153609112819
ICEYE-X36
1 59103U 24043F 26158.68825802 .00004079 00000+0 27826-3 0 9999
2 59103 97.8355 296.5937 0005746 7.3897 352.7413 15.06793192123716
ICEYE-X36
1 59103U 24043F 26159.35233280 .00003366 00000+0 23040-3 0 9999
2 59103 97.8353 297.2694 0005687 4.3753 355.7522 15.06796494123817
ICEYE-X36
1 59103U 24043F 26166.92262582 .00003618 00000+0 24691-3 0 9990
2 59103 97.8373 304.9710 0005108 336.3826 23.7165 15.06858602124952
ICEYE-X36
1 59103U 24043F 26255.14828070 .00003049 00000-0 20468-3 0 9990
2 59103 97.8486 34.8692 0006466 36.6781 323.4888 15.07597002138247
ICEYE-X36
1 59103U 24043F 26260.59073212 .00003209 00000-0 21497-3 0 9996
2 59103 97.8501 40.4232 0006153 21.4151 338.7333 15.07633492139063
ICEYE-X36
1 59103U 24043F 26261.71902949 .00002764 00000-0 18576-3 0 9998
2 59103 97.8500 41.5746 0006060 18.1231 342.0211 15.07639150139232
ICEYE-X36
1 59103U 24043F 26262.78095287 .00002216 00000-0 14977-3 0 9997
2 59103 97.8499 42.6583 0005964 14.9606 345.1797 15.07643239139395
ICEYE-X36
1 59103U 24043F 26263.57739316 .00002113 00000-0 14303-3 0 9992
2 59103 97.8498 43.4711 0005895 12.3871 347.7500 15.07646792139516
ICEYE-X36
1 59103U 24043F 26264.30746210 .00001920 00000-0 13036-3 0 9992
2 59103 97.8497 44.2161 0005836 9.7865 350.3475 15.07649397139628
ICEYE-X36
1 59103U 24043F 26265.03752949 .00001861 00000-0 12644-3 0 9997
2 59103 97.8497 44.9611 0005772 7.0979 353.0329 15.07652335139737
ICEYE-X36
1 59103U 24043F 26266.36492184 .00002136 00000-0 14447-3 0 9995
2 59103 97.8497 46.3158 0005664 1.7767 358.3479 15.07658162139935
ICEYE-X36
1 59103U 24043F 26266.82950772 .00002606 00000-0 17530-3 0 9996
2 59103 97.8498 46.7899 0005624 359.8800 0.2424 15.07661498140002
ICEYE-X36
1 59103U 24043F 26268.55510567 .00003708 00000-0 24744-3 0 9991
2 59103 97.8503 48.5513 0005525 352.8204 7.2943 15.07672947140265
ICEYE-X36
1 59103U 24043F 26269.55063681 .00004055 00000-0 27015-3 0 9992
2 59103 97.8507 49.5673 0005452 348.1373 11.9725 15.07679449140412
ICEYE-X36
1 59103U 24043F 26270.28068919 .00004003 00000-0 26673-3 0 9995
2 59103 97.8511 50.3126 0005443 345.0274 15.0791 15.07685221140521
ICEYE-X36
1 59103U 24043F 26271.60805178 .00003113 00000-0 20835-3 0 9993
2 59103 97.8515 51.6677 0005238 341.1568 18.9464 15.07692503140727
ICEYE-X36
1 59103U 24043F 26272.80267229 .00002578 00000-0 17326-3 0 9990
2 59103 97.8520 52.8874 0005148 337.4380 22.6620 15.07698623140900
ICEYE-X36
1 59103U 24043F 26273.59908483 .00002526 00000-0 16987-3 0 9996
2 59103 97.8517 53.7006 0005124 334.7779 25.3197 15.07701220141020
ICEYE-X36
1 59103U 24043F 26274.59459599 .00002356 00000-0 15872-3 0 9999
2 59103 97.8519 54.7168 0005042 330.9518 29.1428 15.07706167141170
ICEYE-X36
1 59103U 24043F 26275.85557286 .00002149 00000-0 14515-3 0 9994
2 59103 97.8520 56.0042 0004958 326.1509 33.9401 15.07711702141366
ICEYE-X36
1 59103U 24043F 26276.58561018 .00002146 00000-0 14495-3 0 9997
2 59103 97.8521 56.7496 0004921 323.3047 36.7843 15.07715077141475
ICEYE-X36
1 59103U 24043F 26277.58111317 .00002116 00000-0 14295-3 0 9996
2 59103 97.8521 57.7660 0004867 319.4078 40.6785 15.07719422141622
ICEYE-X36
1 59103U 24043F 26278.64297961 .00002417 00000-0 16265-3 0 9997
2 59103 97.8519 58.8502 0004685 315.9000 44.1853 15.07725514141781
ICEYE-X36
1 59103U 24043F 26279.57210904 .00002277 00000-0 15345-3 0 9994
2 59103 97.8518 59.7988 0004670 311.6646 48.4181 15.07729980141929
ICEYE-X36
1 59103U 24043F 26280.56760193 .00002036 00000-0 13765-3 0 9992
2 59103 97.8518 60.8152 0004668 307.1200 52.9600 15.07734422142078
ICEYE-X36
1 59103U 24043F 26281.56309204 .00002593 00000-0 17409-3 0 9992
2 59103 97.8520 61.8317 0004677 302.4752 57.6023 15.07740758142222
ICEYE-X36
1 59103U 24043F 26281.82855571 .00002832 00000-0 18970-3 0 9994
2 59103 97.8521 62.1027 0004687 301.2023 58.8744 15.07742590142267
ICEYE-X36
1 59103U 24043F 26282.16038467 .00002473 00000-0 16620-3 0 9990
2 59103 97.8522 62.4416 0004691 299.7095 60.3665 15.07742989142316
ICEYE-X36
1 59103U 24043F 26283.62042731 .00003762 00000-0 25050-3 0 9990
2 59103 97.8527 63.9326 0004705 293.2618 66.8114 15.07754202142538
ICEYE-X36
1 59103U 24043F 26284.61590186 .00005963 00000-0 39419-3 0 9991
2 59103 97.8530 64.9492 0004745 288.8173 71.2539 15.07767500142689
ICEYE-X36
1 59103U 24043F 26285.27954860 .00004849 00000-0 32139-3 0 9991
2 59103 97.8533 65.6269 0004726 286.1660 73.9047 15.07771501142783
ICEYE-X36
1 59103U 24043F 26286.60683222 .00004622 00000-0 30645-3 0 9999
2 59103 97.8539 66.9826 0004656 280.5005 79.5697 15.07785006142985
ICEYE-X36
1 59103U 24043F 26287.27047001 .00004244 00000-0 28171-3 0 9993
2 59103 97.8541 67.6605 0004622 277.7934 82.2768 15.07790448143081
ICEYE-X36
1 59103U 24043F 26288.66410193 .00003889 00000-0 25844-3 0 9991
2 59103 97.8545 69.0840 0004529 271.8666 88.2043 15.07801823143290
ICEYE-X36
1 59103U 24043F 26288.92955421 .00004098 00000+0 27211-3 0 9990
2 59103 97.8545 69.3551 0004502 270.6218 89.4494 15.07804721143331
ICEYE-X36
1 59103U 24043F 26289.85863499 .00003871 00000-0 25722-3 0 9990
2 59103 97.8545 70.3041 0004450 266.4128 93.6590 15.07811918143477
ICEYE-X36
1 59103U 24043F 26289.85863500 .00003814 00000-0 25347-3 0 9993
2 59103 97.8545 70.3041 0004447 266.3883 93.6835 15.07811854143471
ICEYE-X36
1 59103U 24043F 26290.05772307 .00003989 00000-0 26490-3 0 9994
2 59103 97.8544 70.5074 0004441 265.4495 94.6225 15.07813723143506
ICEYE-X36
1 59103U 24043F 26290.19044830 .00004188 00000-0 27790-3 0 9998
2 59103 97.8544 70.6430 0004435 264.8939 95.1782 15.07815295143524
ICEYE-X36
1 59103U 24043F 26290.32317349 .00004105 00000-0 27248-3 0 9998
2 59103 97.8544 70.7786 0004431 264.3247 95.7475 15.07816338143548
ICEYE-X36
1 59103U 24043F 26290.38953603 .00004145 00000-0 27503-3 0 9991
2 59103 97.8544 70.8464 0004430 263.9776 96.0946 15.07817008143556
ICEYE-X36
1 59103U 24043F 26290.52226120 .00003839 00000-0 25506-3 0 9994
2 59103 97.8544 70.9820 0004425 263.2954 96.7769 15.07817240143578
ICEYE-X36
1 59103U 24043F 26290.85407330 .00004039 00000-0 26811-3 0 9997
2 59103 97.8543 71.3209 0004420 261.9119 98.1606 15.07820589143628
ICEYE-X36
1 59103U 24043F 26290.98679776 .00004571 00000-0 30280-3 0 9992
2 59103 97.8543 71.4564 0004423 261.4686 98.6040 15.07822695143649
ICEYE-X36
1 59103U 24043F 26291.18588521 .00003960 00000-0 26292-3 0 9991
2 59103 97.8542 71.6597 0004427 260.5670 99.5056 15.07822602143671
ICEYE-X36
1 59103U 24043F 26291.31860976 .00003914 00000-0 25995-3 0 9991
2 59103 97.8542 71.7953 0004429 260.0318 100.0410 15.07823629143698
ICEYE-X36
1 59103U 24043F 26291.51769637 .00003838 00000-0 25493-3 0 9993
2 59103 97.8542 71.9987 0004428 259.1266 100.9463 15.07825171143722
ICEYE-X36
1 59103U 24043F 26291.65042070 .00003903 00000-0 25923-3 0 9994
2 59103 97.8541 72.1342 0004427 258.5532 101.5199 15.07826318143746
ICEYE-X36
1 59103U 24043F 26291.84950712 .00003783 00000-0 25134-3 0 9996
2 59103 97.8541 72.3375 0004428 257.6442 102.4290 15.07827581143773
ICEYE-X36
1 59103U 24043F 26291.91586913 .00003881 00000-0 25774-3 0 9991
2 59103 97.8541 72.4053 0004426 257.2678 102.8055 15.07828340143780
ICEYE-X36
1 59103U 24043F 26292.18131736 .00003704 00000-0 24618-3 0 9990
2 59103 97.8540 72.6765 0004435 256.1254 103.9480 15.07829570143824
ICEYE-X36
1 59103U 24043F 26292.31404130 .00003675 00000-0 24428-3 0 9992
2 59103 97.8540 72.8120 0004445 255.4996 104.5739 15.07830592143849
ICEYE-X36
1 59103U 24043F 26292.51312700 .00003657 00000-0 24311-3 0 9996
2 59103 97.8540 73.0154 0004451 254.5213 105.5523 15.07832036143878
ICEYE-X36
1 59103U 24043F 26292.84493628 .00003709 00000-0 24646-3 0 9990
2 59103 97.8539 73.3543 0004476 253.0298 107.0439 15.07834455143921
ICEYE-X36
1 59103U 24043F 26293.44219192 .00003376 00000-0 22475-3 0 9997
2 59103 97.8538 73.9643 0004546 250.9081 109.1654 15.07837575144010
ICEYE-X36
1 59103U 24043F 26294.83578209 .00003518 00000-0 23393-3 0 9996
2 59103 97.8534 75.3877 0004672 244.9804 115.0939 15.07846787144227
ICEYE-X36
1 59103U 24043F 26295.83119797 .00003685 00000-0 24476-3 0 9998
2 59103 97.8534 76.4045 0004779 241.2210 118.8537 15.07854273144375
ICEYE-X36
1 59103U 24043F 26296.82660950 .00003555 00000-0 23623-3 0 9999
2 59103 97.8536 77.4214 0004866 237.7481 122.3275 15.07861103144529
ICEYE-X36
1 59103U 24043F 26297.22477300 .00003484 00000+0 23160-3 0 9995
2 59103 97.8537 77.8281 0004894 236.2883 123.7878 15.07863566144584
ICEYE-X36
1 59103U 24043F 26297.22477300 .00003484 00000-0 23160-3 0 9996
2 59103 97.8537 77.8281 0004895 236.2883 123.7878 15.07863566144585
ICEYE-X36
1 59103U 24043F 26298.68469998 .00003088 00000-0 20571-3 0 9994
2 59103 97.8545 79.3198 0004979 230.3517 129.7271 15.07872062144809
ICEYE-X36
1 59103U 24043F 26299.21558029 .00002859 00000-0 19077-3 0 9992
2 59103 97.8547 79.8622 0005010 228.4636 131.6162 15.07874757144887
ICEYE-X36
1 59103U 24043F 26311.22646569 .00005238 00000-0 34511-3 0 09999
2 59103 97.8549 92.1350 0005747 184.5121 175.6056 15.07948998146693
ICEYE-X36
1 59103U 24043F 26311.89003246 .00006200 00000-0 40753-3 0 9996
2 59103 97.8551 92.8132 0005791 182.5432 177.5767 15.07957163146798
+138
View File
@@ -0,0 +1,138 @@
COSMOS 2609
1 68754U 26083B 26152.70872904 .00002576 00000+0 17505-3 0 9992
2 68754 98.1201 300.7511 0012336 120.4488 239.7959 15.07304170 6897
COSMOS 2609
1 68754U 26083B 26159.74542199 .00002602 00000+0 17651-3 0 9995
2 68754 98.1217 308.1752 0012593 99.0372 261.2280 15.07345927 7951
COSMOS 2609
1 68754U 26083B 26167.71129863 .00001705 00000+0 11720-3 0 9994
2 68754 98.1221 316.5801 0012449 74.9313 285.3289 15.07374465 9150
COSMOS 2609
1 68754U 26083B 26171.42865760 .00001950 00000+0 13332-3 0 9993
2 68754 98.1227 320.5028 0012296 63.7881 296.4607 15.07388042 9713
COSMOS 2609
1 68754U 26083B 26260.83772171 .00001889 00000-0 13485-3 0 9991
2 68754 98.1354 54.7766 0012651 78.2307 282.0337 15.05804681 23175
COSMOS 2609
1 68754U 26083B 26261.63513074 .00001689 00000-0 12108-3 0 9994
2 68754 98.1356 55.6175 0012659 75.8927 284.3705 15.05806771 23299
COSMOS 2609
1 68754U 26083B 26262.76479120 .00001398 00000-0 10109-3 0 9998
2 68754 98.1357 56.8087 0012671 72.5451 287.7159 15.05809555 23467
COSMOS 2609
1 68754U 26083B 26263.69509759 .00001263 00000-0 91788-4 0 9997
2 68754 98.1358 57.7898 0012670 69.7721 290.4866 15.05812144 23605
COSMOS 2609
1 68754U 26083B 26264.62540278 .00001092 00000-0 79956-4 0 9995
2 68754 98.1359 58.7708 0012661 66.9675 293.2884 15.05814180 23742
COSMOS 2609
1 68754U 26083B 26265.68860691 .00001069 00000-0 78418-4 0 9992
2 68754 98.1360 59.8920 0012647 63.7205 296.5318 15.05817107 23908
COSMOS 2609
1 68754U 26083B 26266.75180937 .00001035 00000-0 76042-4 0 9993
2 68754 98.1360 61.0133 0012617 60.4631 299.7851 15.05819637 24068
COSMOS 2609
1 68754U 26083B 26267.74855964 .00001064 00000-0 78038-4 0 9991
2 68754 98.1361 62.0644 0012576 57.3610 302.8827 15.05822334 24217
COSMOS 2609
1 68754U 26083B 26268.67885843 .00001135 00000-0 82934-4 0 9992
2 68754 98.1361 63.0456 0012530 54.4397 305.7994 15.05825007 24351
COSMOS 2609
1 68754U 26083B 26269.67560572 .00001132 00000-0 82722-4 0 9994
2 68754 98.1362 64.0968 0012465 51.3289 308.9049 15.05827331 24509
COSMOS 2609
1 68754U 26083B 26270.73880166 .00001022 00000-0 75166-4 0 9999
2 68754 98.1363 65.2180 0012381 47.9832 312.2445 15.05829058 24664
COSMOS 2609
1 68754U 26083B 26271.60264739 .00001004 00000-0 73915-4 0 9998
2 68754 98.1364 66.1291 0012309 45.2217 315.0007 15.05830737 24791
COSMOS 2609
1 68754U 26083B 26273.72903350 .00001055 00000-0 77449-4 0 9994
2 68754 98.1366 68.3717 0012124 38.3566 321.8519 15.05834760 25115
COSMOS 2609
1 68754U 26083B 26274.65932542 .00001208 00000-0 87949-4 0 9998
2 68754 98.1368 69.3530 0012053 35.3565 324.8457 15.05837208 25257
COSMOS 2609
1 68754U 26083B 26275.58961617 .00001303 00000-0 94453-4 0 9990
2 68754 98.1369 70.3342 0011978 32.3930 327.8028 15.05839335 25394
COSMOS 2609
1 68754U 26083B 26276.58635420 .00001462 00000-0 10541-3 0 9992
2 68754 98.1370 71.3855 0011901 29.2495 330.9395 15.05842269 25542
COSMOS 2609
1 68754U 26083B 26277.58308985 .00001570 00000-0 11285-3 0 9992
2 68754 98.1372 72.4369 0011829 26.1294 334.0526 15.05845393 25696
COSMOS 2609
1 68754U 26083B 26278.71272005 .00001621 00000-0 11631-3 0 9993
2 68754 98.1374 73.6285 0011751 22.6263 337.5478 15.05849284 25865
COSMOS 2609
1 68754U 26083B 26279.70945022 .00001533 00000-0 11028-3 0 9991
2 68754 98.1376 74.6800 0011680 19.5487 340.6184 15.05852289 26011
COSMOS 2609
1 68754U 26083B 26280.77262624 .00001379 00000-0 99701-4 0 9993
2 68754 98.1379 75.8015 0011605 16.2593 343.9002 15.05855405 26179
COSMOS 2609
1 68754U 26083B 26281.63645474 .00001500 00000-0 10796-3 0 9999
2 68754 98.1380 76.7128 0011555 13.5395 346.6138 15.05859310 26304
COSMOS 2609
1 68754U 26083B 26281.96869591 .00001572 00000-0 11296-3 0 9998
2 68754 98.1381 77.0633 0011536 12.5007 347.6502 15.05860963 26355
COSMOS 2609
1 68754U 26083B 26282.30093732 .00001417 00000-0 10225-3 0 9992
2 68754 98.1381 77.4138 0011513 11.4362 348.7123 15.05861209 26407
COSMOS 2609
1 68754U 26083B 26282.69962578 .00001523 00000-0 10953-3 0 9993
2 68754 98.1382 77.8344 0011482 10.1907 349.9548 15.05863355 26461
COSMOS 2609
1 68754U 26083B 26283.69634675 .00001558 00000-0 11195-3 0 9995
2 68754 98.1384 78.8860 0011420 7.0157 353.1226 15.05866972 26616
COSMOS 2609
1 68754U 26083B 26284.69306260 .00001868 00000-0 13327-3 0 9995
2 68754 98.1384 79.9376 0011359 3.7945 356.3365 15.05872893 26763
COSMOS 2609
1 68754U 26083B 26285.29109375 .00001759 00000-0 12573-3 0 9991
2 68754 98.1385 80.5685 0011291 2.5721 357.5560 15.05873974 26850
COSMOS 2609
1 68754U 26083B 26286.62004434 .00001614 00000-0 11575-3 0 9990
2 68754 98.1386 81.9707 0011170 358.0409 2.0770 15.05878976 27054
COSMOS 2609
1 68754U 26083B 26287.61675478 .00001297 00000-0 94015-4 0 9990
2 68754 98.1386 83.0224 0011085 354.4382 5.6718 15.05881642 27208
COSMOS 2609
1 68754U 26083B 26288.67991186 .00001324 00000-0 95812-4 0 9991
2 68754 98.1385 84.1440 0011078 350.1976 9.9031 15.05883714 27367
COSMOS 2609
1 68754U 26083B 26288.94570044 .00001361 00000+0 98344-4 0 9990
2 68754 98.1385 84.4244 0011058 349.2550 10.8438 15.05884825 27408
COSMOS 2609
1 68754U 26083B 26289.61017217 .00001341 00000-0 96980-4 0 9996
2 68754 98.1384 85.1255 0011012 346.8712 13.2225 15.05886718 27505
COSMOS 2609
1 68754U 26083B 26290.27464265 .00001428 00000-0 10299-3 0 9993
2 68754 98.1384 85.8265 0010972 344.4623 15.6264 15.05889247 27602
COSMOS 2609
1 68754U 26083B 26291.60358214 .00001675 00000-0 11991-3 0 9991
2 68754 98.1384 87.2287 0010887 339.6211 20.4578 15.05894006 27809
COSMOS 2609
1 68754U 26083B 26292.26805095 .00001832 00000-0 13073-3 0 9996
2 68754 98.1384 87.9298 0010847 337.1519 22.9222 15.05896458 27905
COSMOS 2609
1 68754U 26083B 26293.26475213 .00001872 00000-0 13349-3 0 9992
2 68754 98.1385 88.9814 0010780 333.4795 26.5877 15.05899349 28057
COSMOS 2609
1 68754U 26083B 26294.59368299 .00002052 00000-0 14583-3 0 9996
2 68754 98.1387 90.3836 0010699 328.6668 31.3918 15.05904380 28258
COSMOS 2609
1 68754U 26083B 26295.59037760 .00002154 00000-0 15280-3 0 9991
2 68754 98.1389 91.4353 0010628 325.0265 35.0261 15.05908509 28408
COSMOS 2609
1 68754U 26083B 26296.25483896 .00002152 00000-0 15268-3 0 9994
2 68754 98.1390 92.1365 0010579 322.6332 37.4157 15.05911139 28508
COSMOS 2609
1 68754U 26083B 26297.25152876 .00002089 00000-0 14829-3 0 9997
2 68754 98.1392 93.1882 0010503 319.0562 40.9874 15.05914963 28656
COSMOS 2609
1 68754U 26083B 26298.71333574 .00001706 00000-0 12200-3 0 9991
2 68754 98.1395 94.7308 0010387 313.8286 46.2080 15.05919229 28876
COSMOS 2609
1 68754U 26083B 26299.24490067 .00001512 00000-0 10868-3 0 9994
2 68754 98.1396 95.2918 0010346 311.9169 48.1173 15.05920507 28958
+129
View File
@@ -0,0 +1,129 @@
COSMOS 2610
1 68758U 26083F 26152.72862595 .00003478 00000+0 23983-3 0 9992
2 68758 97.8035 290.4876 0011902 105.5191 254.7350 15.06445751 6874
COSMOS 2610
1 68758U 26083F 26159.70293477 .00002294 00000+0 15951-3 0 9995
2 68758 97.8044 297.5512 0012260 84.6938 275.5687 15.06485307 7925
COSMOS 2610
1 68758U 26083F 26260.84984109 .00001716 00000-0 11933-3 0 9997
2 68758 97.8189 40.1047 0011975 99.1459 261.1123 15.06852447 23153
COSMOS 2610
1 68758U 26083F 26261.64670071 .00001479 00000-0 10343-3 0 9996
2 68758 97.8190 40.9137 0011993 96.7788 263.4804 15.06854153 23271
COSMOS 2610
1 68758U 26083F 26262.77558333 .00001205 00000-0 85066-4 0 9999
2 68758 97.8190 42.0599 0012000 93.3343 266.9256 15.06856478 23446
COSMOS 2610
1 68758U 26083F 26263.70524966 .00001075 00000-0 76368-4 0 9998
2 68758 97.8191 43.0038 0011997 90.5207 269.7395 15.06858624 23583
COSMOS 2610
1 68758U 26083F 26264.63491513 .00000996 00000-0 71110-4 0 9993
2 68758 97.8192 43.9477 0011985 87.7216 272.5383 15.06860574 23727
COSMOS 2610
1 68758U 26083F 26265.63098391 .00001128 00000-0 79941-4 0 9999
2 68758 97.8193 44.9591 0011963 84.7349 275.5243 15.06863556 23873
COSMOS 2610
1 68758U 26083F 26266.75986014 .00001324 00000-0 93025-4 0 9998
2 68758 97.8195 46.1054 0011928 81.3433 278.9145 15.06866971 24043
COSMOS 2610
1 68758U 26083F 26267.75592490 .00001597 00000-0 11132-3 0 9990
2 68758 97.8198 47.1168 0011891 78.3528 281.9033 15.06870739 24196
COSMOS 2610
1 68758U 26083F 26268.68558311 .00001890 00000-0 13090-3 0 9998
2 68758 97.8200 48.0609 0011856 75.5517 284.7024 15.06874737 24337
COSMOS 2610
1 68758U 26083F 26269.68164297 .00002066 00000-0 14264-3 0 9995
2 68758 97.8202 49.0723 0011815 72.6147 287.6371 15.06878638 24486
COSMOS 2610
1 68758U 26083F 26270.81050765 .00002052 00000-0 14170-3 0 9997
2 68758 97.8205 50.2188 0011769 69.2625 290.9862 15.06882383 24652
COSMOS 2610
1 68758U 26083F 26271.60735111 .00002028 00000-0 14009-3 0 9994
2 68758 97.8206 51.0280 0011738 66.9087 293.3376 15.06885228 24778
COSMOS 2610
1 68758U 26083F 26273.73225854 .00001660 00000-0 11546-3 0 9992
2 68758 97.8211 53.1861 0011669 60.6152 299.6238 15.06891907 25092
COSMOS 2610
1 68758U 26083F 26274.66190204 .00001547 00000-0 10786-3 0 9994
2 68758 97.8212 54.1303 0011639 57.8469 302.3885 15.06894976 25236
COSMOS 2610
1 68758U 26083F 26275.59154395 .00001361 00000-0 95456-4 0 9990
2 68758 97.8214 55.0745 0011609 55.0681 305.1634 15.06897345 25372
COSMOS 2610
1 68758U 26083F 26276.65398968 .00001285 00000-0 90334-4 0 9999
2 68758 97.8215 56.1537 0011569 51.8609 308.3658 15.06900445 25538
COSMOS 2610
1 68758U 26083F 26277.58362798 .00001188 00000-0 83870-4 0 9993
2 68758 97.8216 57.0980 0011527 49.0105 311.2117 15.06902796 25674
COSMOS 2610
1 68758U 26083F 26278.71247184 .00001130 00000-0 79996-4 0 9991
2 68758 97.8217 58.2447 0011471 45.5151 314.7011 15.06905918 25845
COSMOS 2610
1 68758U 26083F 26279.70850916 .00001044 00000-0 74195-4 0 9994
2 68758 97.8218 59.2565 0011417 42.3824 317.8283 15.06908206 25994
COSMOS 2610
1 68758U 26083F 26280.77094721 .00000937 00000-0 67062-4 0 9992
2 68758 97.8219 60.3357 0011347 39.0190 321.1854 15.06910458 26151
COSMOS 2610
1 68758U 26083F 26281.63417664 .00001217 00000-0 85762-4 0 9992
2 68758 97.8220 61.2126 0011295 36.2545 323.9445 15.06914174 26281
COSMOS 2610
1 68758U 26083F 26281.96618754 .00001333 00000-0 93516-4 0 9996
2 68758 97.8221 61.5499 0011271 35.1938 325.0031 15.06915688 26337
COSMOS 2610
1 68758U 26083F 26282.69661095 .00001407 00000-0 98466-4 0 9994
2 68758 97.8222 62.2919 0011214 32.8393 327.3529 15.06917967 26440
COSMOS 2610
1 68758U 26083F 26283.69264145 .00001680 00000-0 11674-3 0 9998
2 68758 97.8224 63.3038 0011131 29.5994 330.5861 15.06921909 26596
COSMOS 2610
1 68758U 26083F 26284.75506972 .00002767 00000-0 18933-3 0 9995
2 68758 97.8223 64.3829 0010961 25.8941 334.2832 15.06927652 26753
COSMOS 2610
1 68758U 26083F 26285.28628350 .00002499 00000-0 17141-3 0 9991
2 68758 97.8224 64.9226 0010917 24.0469 336.1265 15.06928628 26832
COSMOS 2610
1 68758U 26083F 26286.61431176 .00002865 00000-0 19583-3 0 9996
2 68758 97.8229 66.2719 0010811 19.6031 340.5609 15.06936248 27038
COSMOS 2610
1 68758U 26083F 26287.61032854 .00002778 00000-0 19000-3 0 9994
2 68758 97.8232 67.2840 0010743 16.3763 343.7809 15.06941500 27188
COSMOS 2610
1 68758U 26083F 26288.73914347 .00002629 00000-0 18004-3 0 9992
2 68758 97.8235 68.4310 0010668 12.8500 347.2996 15.06947198 27356
COSMOS 2610
1 68758U 26083F 26288.93834545 .00002660 00000+0 18210-3 0 9998
2 68758 97.8235 68.6334 0010653 12.2371 347.9112 15.06948519 27386
COSMOS 2610
1 68758U 26083F 26289.66875211 .00002554 00000-0 17501-3 0 9999
2 68758 97.8236 69.3755 0010598 10.0169 350.1267 15.06952212 27495
COSMOS 2610
1 68758U 26083F 26290.33275620 .00002595 00000-0 17776-3 0 9992
2 68758 97.8237 70.0503 0010549 8.0135 352.1259 15.06956191 27596
COSMOS 2610
1 68758U 26083F 26291.66076058 .00002485 00000-0 17035-3 0 9999
2 68758 97.8238 71.3997 0010441 4.0387 356.0922 15.06962777 27793
COSMOS 2610
1 68758U 26083F 26292.32476102 .00002353 00000-0 16153-3 0 9991
2 68758 97.8237 72.0744 0010386 1.9824 358.1443 15.06965458 27891
COSMOS 2610
1 68758U 26083F 26293.32075912 .00002062 00000-0 14213-3 0 9998
2 68758 97.8237 73.0865 0010292 358.8694 1.2507 15.06969043 28040
COSMOS 2610
1 68758U 26083F 26294.64875251 .00001922 00000-0 13274-3 0 9998
2 68758 97.8237 74.4359 0010162 354.4951 5.6162 15.06974085 28246
COSMOS 2610
1 68758U 26083F 26295.64474431 .00001805 00000-0 12495-3 0 9998
2 68758 97.8238 75.4481 0010070 351.0065 9.0980 15.06977834 28398
COSMOS 2610
1 68758U 26083F 26296.30873745 .00001677 00000-0 11638-3 0 9998
2 68758 97.8238 76.1229 0010019 348.5573 11.5425 15.06980145 28496
COSMOS 2610
1 68758U 26083F 26297.30472565 .00001334 00000-0 93484-4 0 9994
2 68758 97.8238 77.1351 0009943 344.8023 15.2904 15.06982792 28646
COSMOS 2610
1 68758U 26083F 26298.69910817 .00001187 00000-0 83681-4 0 9998
2 68758 97.8245 78.5524 0009902 339.3585 20.7240 15.06984851 28853
COSMOS 2610
1 68758U 26083F 26299.29669974 .00001076 00000-0 76253-4 0 9994
2 68758 97.8245 79.1598 0009861 337.1406 22.9380 15.06985925 28945
+129
View File
@@ -0,0 +1,129 @@
COSMOS 2611
1 68759U 26083G 26159.71053314 .00002533 00000+0 17718-3 0 9990
2 68759 97.8106 297.0050 0013014 90.2940 269.9778 15.06157628 7910
COSMOS 2611
1 68759U 26083G 26260.87971400 .00001968 00000-0 13737-3 0 9994
2 68759 97.8246 39.6071 0012602 105.1572 255.1050 15.06513645 23145
COSMOS 2611
1 68759U 26083G 26261.61033398 .00001958 00000-0 13670-3 0 9997
2 68759 97.8247 40.3490 0012611 103.0092 257.2544 15.06515762 23259
COSMOS 2611
1 68759U 26083G 26262.80589069 .00001906 00000-0 13321-3 0 9996
2 68759 97.8249 41.5631 0012629 99.4504 260.8151 15.06519529 23435
COSMOS 2611
1 68759U 26083G 26263.73576495 .00001882 00000-0 13156-3 0 9997
2 68759 97.8251 42.5075 0012650 96.6842 263.5825 15.06523006 23577
COSMOS 2611
1 68759U 26083G 26264.59921772 .00001799 00000-0 12594-3 0 9996
2 68759 97.8253 43.3844 0012671 94.1102 266.1573 15.06525950 23701
COSMOS 2611
1 68759U 26083G 26265.72834500 .00001825 00000-0 12768-3 0 9997
2 68759 97.8256 44.5312 0012699 90.7079 269.5602 15.06530662 23876
COSMOS 2611
1 68759U 26083G 26266.72463106 .00001778 00000-0 12448-3 0 9990
2 68759 97.8259 45.5431 0012718 87.6902 272.5780 15.06534452 24024
COSMOS 2611
1 68759U 26083G 26267.78733273 .00001783 00000-0 12480-3 0 9994
2 68759 97.8262 46.6225 0012735 84.4681 275.7998 15.06538841 24188
COSMOS 2611
1 68759U 26083G 26268.71719412 .00001824 00000-0 12754-3 0 9997
2 68759 97.8264 47.5670 0012743 81.6582 278.6088 15.06542834 24327
COSMOS 2611
1 68759U 26083G 26269.71347191 .00001697 00000-0 11901-3 0 9990
2 68759 97.8265 48.5790 0012741 78.6577 281.6080 15.06546084 24473
COSMOS 2611
1 68759U 26083G 26270.77616603 .00001346 00000-0 95279-4 0 9997
2 68759 97.8270 49.6584 0012866 75.7764 284.4890 15.06549610 24632
COSMOS 2611
1 68759U 26083G 26271.63960356 .00001251 00000-0 88874-4 0 9998
2 68759 97.8267 50.5356 0012710 72.8000 287.4617 15.06550538 24760
COSMOS 2611
1 68759U 26083G 26273.69856644 .00000861 00000-0 62580-4 0 9992
2 68759 97.8268 52.6272 0012614 66.4409 293.8141 15.06554086 25076
COSMOS 2611
1 68759U 26083G 26274.62841921 .00000889 00000-0 64436-4 0 9990
2 68759 97.8269 53.5718 0012558 63.5744 296.6770 15.06556131 25217
COSMOS 2611
1 68759U 26083G 26275.62468937 .00000946 00000-0 68264-4 0 9990
2 68759 97.8270 54.5840 0012493 60.4847 299.7623 15.06558013 25362
COSMOS 2611
1 68759U 26083G 26276.62095821 .00001162 00000-0 82847-4 0 9993
2 68759 97.8271 55.5961 0012424 57.4149 302.8276 15.06560609 25514
COSMOS 2611
1 68759U 26083G 26277.55080763 .00001356 00000-0 95950-4 0 9999
2 68759 97.8272 56.5408 0012356 54.5445 305.6934 15.06563165 25650
COSMOS 2611
1 68759U 26083G 26278.74632481 .00001624 00000-0 11399-3 0 9993
2 68759 97.8275 57.7554 0012276 50.8670 309.3645 15.06567283 25833
COSMOS 2611
1 68759U 26083G 26279.74258669 .00001728 00000-0 12100-3 0 9992
2 68759 97.8277 58.7677 0012209 47.8019 312.4242 15.06570547 25982
COSMOS 2611
1 68759U 26083G 26280.80526284 .00001729 00000-0 12105-3 0 9998
2 68759 97.8279 59.8474 0012141 44.5619 315.6581 15.06574097 26140
COSMOS 2611
1 68759U 26083G 26281.66868447 .00002002 00000-0 13946-3 0 9992
2 68759 97.8282 60.7247 0012095 41.8892 318.3258 15.06578914 26271
COSMOS 2611
1 68759U 26083G 26281.93435211 .00002097 00000-0 14589-3 0 9999
2 68759 97.8283 60.9947 0012081 41.0732 319.1402 15.06580592 26311
COSMOS 2611
1 68759U 26083G 26282.33285373 .00001930 00000-0 13462-3 0 9990
2 68759 97.8283 61.3996 0012056 39.8456 320.3654 15.06581253 26374
COSMOS 2611
1 68759U 26083G 26282.73135394 .00002069 00000-0 14397-3 0 9993
2 68759 97.8285 61.8045 0012035 38.6414 321.5671 15.06583877 26437
COSMOS 2611
1 68759U 26083G 26283.72760367 .00002165 00000-0 15042-3 0 9992
2 68759 97.8288 62.8169 0011984 35.5717 324.6307 15.06588728 26588
COSMOS 2611
1 68759U 26083G 26284.72384823 .00002805 00000-0 19357-3 0 9996
2 68759 97.8292 63.8291 0011976 32.8791 327.3178 15.06596383 26732
COSMOS 2611
1 68759U 26083G 26285.32159440 .00002299 00000-0 15943-3 0 9992
2 68759 97.8294 64.4367 0011947 31.0909 329.1022 15.06597603 26829
COSMOS 2611
1 68759U 26083G 26286.64991358 .00002188 00000-0 15192-3 0 9993
2 68759 97.8297 65.7868 0011870 27.1212 333.0632 15.06604131 27029
COSMOS 2611
1 68759U 26083G 26287.64614964 .00001865 00000-0 13017-3 0 9994
2 68759 97.8297 66.7994 0011788 24.0114 336.1660 15.06607829 27173
COSMOS 2611
1 68759U 26083G 26288.70879903 .00001627 00000-0 11409-3 0 9997
2 68759 97.8297 67.8794 0011690 20.5283 339.6411 15.06611269 27336
COSMOS 2611
1 68759U 26083G 26288.97446075 .00001646 00000+0 11536-3 0 9997
2 68759 97.8296 68.1494 0011662 19.6184 340.5489 15.06612455 27372
COSMOS 2611
1 68759U 26083G 26289.63861492 .00001556 00000-0 10932-3 0 9995
2 68759 97.8295 68.8244 0011598 17.3523 342.8097 15.06614540 27479
COSMOS 2611
1 68759U 26083G 26290.30276783 .00001611 00000-0 11301-3 0 9997
2 68759 97.8293 69.4994 0011533 14.9763 345.1803 15.06617154 27570
COSMOS 2611
1 68759U 26083G 26291.63107155 .00001671 00000-0 11709-3 0 9998
2 68759 97.8290 70.8493 0011396 10.1904 349.9551 15.06621464 27772
COSMOS 2611
1 68759U 26083G 26292.29522244 .00001722 00000-0 12048-3 0 9995
2 68759 97.8289 71.5242 0011332 7.7228 352.4171 15.06623372 27879
COSMOS 2611
1 68759U 26083G 26293.29144718 .00001854 00000-0 12938-3 0 9996
2 68759 97.8295 72.5368 0011307 4.2435 355.8886 15.06627614 28021
COSMOS 2611
1 68759U 26083G 26294.61974244 .00002055 00000-0 14287-3 0 9991
2 68759 97.8297 73.8869 0011181 359.5921 0.5295 15.06632873 28222
COSMOS 2611
1 68759U 26083G 26295.61596169 .00002529 00000-0 17483-3 0 9995
2 68759 97.8291 74.8991 0011060 355.6003 4.5125 15.06636043 28372
COSMOS 2611
1 68759U 26083G 26296.28010552 .00002596 00000-0 17930-3 0 9997
2 68759 97.8293 75.5742 0011018 353.2762 6.8315 15.06639287 28475
COSMOS 2611
1 68759U 26083G 26297.27631845 .00002482 00000-0 17164-3 0 9994
2 68759 97.8297 76.5869 0010941 349.8959 10.2046 15.06643963 28626
COSMOS 2611
1 68759U 26083G 26298.73742535 .00001887 00000-0 13154-3 0 9998
2 68759 97.8306 78.0726 0010793 345.2900 14.8011 15.06648313 28847
COSMOS 2611
1 68759U 26083G 26299.26873591 .00001733 00000-0 12119-3 0 9994
2 68759 97.8307 78.6128 0010749 343.4743 16.6132 15.06649756 28921
+153
View File
@@ -0,0 +1,153 @@
COSMOS 2612
1 68762U 26083H 26155.90287272 .00002690 00000+0 18390-3 0 9994
2 68762 97.8530 293.7591 0014565 92.1716 268.1179 15.06956289 7356
COSMOS 2612
1 68762U 26083H 26159.75407367 .00002135 00000+0 14684-3 0 9996
2 68762 97.8522 297.6867 0014348 80.4063 279.8784 15.06972914 7938
COSMOS 2612
1 68762U 26083H 26171.44020111 .00001567 00000+0 10885-3 0 9991
2 68762 97.8544 309.6054 0013827 44.2942 315.9388 15.07023515 9691
COSMOS 2612
1 68762U 26083H 26260.86758294 .00002233 00000-0 15183-3 0 9994
2 68762 97.8674 40.9197 0014205 95.7247 264.5600 15.07354460 23168
COSMOS 2612
1 68762U 26083H 26261.59779413 .00002048 00000-0 13963-3 0 9999
2 68762 97.8675 41.6662 0014214 93.5134 266.7719 15.07356643 23277
COSMOS 2612
1 68762U 26083H 26262.79268196 .00001736 00000-0 11898-3 0 9991
2 68762 97.8677 42.8878 0014229 89.8825 270.4032 15.07360212 23459
COSMOS 2612
1 68762U 26083H 26263.72203639 .00001508 00000-0 10392-3 0 9991
2 68762 97.8678 43.8380 0014235 87.0399 273.2457 15.07363086 23591
COSMOS 2612
1 68762U 26083H 26264.65138936 .00001311 00000-0 90972-4 0 9996
2 68762 97.8679 44.7882 0014238 84.1896 276.0954 15.07365540 23738
COSMOS 2612
1 68762U 26083H 26265.71350497 .00001300 00000-0 90218-4 0 9993
2 68762 97.8680 45.8742 0014234 80.8835 279.4002 15.07369086 23896
COSMOS 2612
1 68762U 26083H 26266.77561874 .00001291 00000-0 89592-4 0 9995
2 68762 97.8682 46.9602 0014220 77.5856 282.6961 15.07372236 24056
COSMOS 2612
1 68762U 26083H 26267.77134797 .00001419 00000-0 98072-4 0 9991
2 68762 97.8684 47.9783 0014198 74.4763 285.8030 15.07375851 24201
COSMOS 2612
1 68762U 26083H 26268.70069343 .00001593 00000-0 10953-3 0 9993
2 68762 97.8686 48.9286 0014169 71.5962 288.6804 15.07379466 24340
COSMOS 2612
1 68762U 26083H 26269.69641887 .00001644 00000-0 11291-3 0 9994
2 68762 97.8685 49.9465 0014102 68.7395 291.5337 15.07381940 24494
COSMOS 2612
1 68762U 26083H 26270.75852367 .00001612 00000-0 11077-3 0 9997
2 68762 97.8688 51.0327 0014008 65.3516 294.9168 15.07385028 24659
COSMOS 2612
1 68762U 26083H 26271.62148238 .00001673 00000-0 11480-3 0 9994
2 68762 97.8691 51.9154 0014029 62.6118 297.6534 15.07388326 24786
COSMOS 2612
1 68762U 26083H 26273.68247567 .00000811 00000-0 84943-4 0 9995
2 68762 97.8688 54.0158 0009837 239.4926 120.5322 14.92325354 25091
COSMOS 2612
1 68762U 26083H 26274.62118805 .00000863 00000-0 90072-4 0 9999
2 68762 97.8691 54.9532 0009851 235.9877 124.0406 14.92325532 25238
COSMOS 2612
1 68762U 26083H 26275.62695029 .00000615 00000-0 65754-4 0 9992
2 68762 97.8692 55.9580 0009897 232.2465 127.7858 14.92326645 25389
COSMOS 2612
1 68762U 26083H 26276.63271178 .00000534 00000-0 57914-4 0 9998
2 68762 97.8694 56.9628 0009946 228.5318 131.5047 14.92328027 25531
COSMOS 2612
1 68762U 26083H 26277.63847267 .00000482 00000-0 52779-4 0 9993
2 68762 97.8695 57.9676 0009999 224.7981 135.2431 14.92329266 25688
COSMOS 2612
1 68762U 26083H 26278.71128329 .00000522 00000-0 56735-4 0 9995
2 68762 97.8697 59.0395 0010063 220.8688 139.1777 14.92330931 25841
COSMOS 2612
1 68762U 26083H 26279.71704291 .00000574 00000-0 61753-4 0 9997
2 68762 97.8699 60.0444 0010133 217.2289 142.8228 14.92332289 25991
COSMOS 2612
1 68762U 26083H 26280.78985167 .00000785 00000-0 82448-4 0 9997
2 68762 97.9003 61.1183 0010228 213.4540 146.6034 14.92334849 26156
COSMOS 2612
1 68762U 26083H 26281.66150731 .00000839 00000-0 87750-4 0 9997
2 68762 97.9005 61.9925 0010285 210.4383 149.6240 14.92337159 26288
COSMOS 2612
1 68762U 26083H 26281.92970876 .00000969 00000-0 10044-3 0 9991
2 68762 97.9005 62.2615 0010305 209.4903 150.5735 14.92338079 26324
COSMOS 2612
1 68762U 26083H 26282.73431255 .00001102 00000-0 11340-3 0 9994
2 68762 97.9007 63.0685 0010362 206.7000 153.3686 14.92339854 26444
COSMOS 2612
1 68762U 26083H 26283.74006574 .00001271 00000-0 12990-3 0 9997
2 68762 97.9009 64.0773 0010433 203.2304 156.8444 14.92342594 26594
COSMOS 2612
1 68762U 26083H 26284.74581481 .00001555 00000-0 15767-3 0 9993
2 68762 97.9010 65.0862 0010502 199.7743 160.3069 14.92347040 26740
COSMOS 2612
1 68762U 26083H 26284.94696506 .00001431 00000-0 14552-3 0 9994
2 68762 97.9011 65.2879 0010518 199.0879 160.9947 14.92346808 26776
COSMOS 2612
1 68762U 26083H 26286.60754176 .00003113 00000-0 20748-3 0 9990
2 68762 97.8531 66.9813 0004629 289.3941 70.6786 15.07850417 27027
COSMOS 2612
1 68762U 26083H 26287.66931602 .00002747 00000-0 18360-3 0 9997
2 68762 97.8534 68.0658 0004555 285.0033 75.0689 15.07856291 27187
COSMOS 2612
1 68762U 26083H 26288.73075855 .00002568 00000-0 16852-3 0 9990
2 68762 97.8543 69.1543 0007699 273.5225 86.5122 15.08583912 27344
COSMOS 2612
1 68762U 26083H 26288.99607294 .00005758 00000+0 37233-3 0 9995
2 68762 97.8548 69.4259 0007586 272.3400 87.6959 15.08588279 27388
COSMOS 2612
1 68762U 26083H 26289.59318872 .00001558 00000-0 10604-3 0 9991
2 68762 97.8549 70.0366 0004576 255.0538 105.0183 15.07839734 27470
COSMOS 2612
1 68762U 26083H 26289.79227700 .00002282 00000-0 15345-3 0 9994
2 68762 97.8552 70.2402 0004640 252.3357 107.7364 15.07815618 27507
COSMOS 2612
1 68762U 26083H 26290.32317661 .00002533 00000-0 16976-3 0 9991
2 68762 97.8543 70.7797 0004438 264.6717 95.4004 15.07822656 27582
COSMOS 2612
1 68762U 26083H 26290.72134983 .00002215 00000-0 14901-3 0 9992
2 68762 97.8545 71.1849 0004479 262.1722 97.8997 15.07823289 27648
COSMOS 2612
1 68762U 26083H 26291.31861747 .00002270 00000-0 15278-3 0 9992
2 68762 97.8541 71.7953 0004465 259.9972 100.0752 15.07776700 27733
COSMOS 2612
1 68762U 26083H 26291.51771032 .00002106 00000-0 14210-3 0 9994
2 68762 97.8541 71.9986 0004460 258.9279 101.1447 15.07777458 27762
COSMOS 2612
1 68762U 26083H 26291.98249573 .00001307 00000-0 91664-4 0 9996
2 68762 97.8542 72.4721 0004032 300.1157 59.9670 15.07011839 27836
COSMOS 2612
1 68762U 26083H 26292.31448618 .00002307 00000-0 15841-3 0 9998
2 68762 97.8540 72.8115 0003973 302.2888 57.7953 15.07013899 27885
COSMOS 2612
1 68762U 26083H 26293.31019816 .00001134 00000-0 78377-4 0 9994
2 68762 97.8542 73.8278 0006285 264.0700 95.9811 15.07841994 28035
COSMOS 2612
1 68762U 26083H 26294.63742370 .00001869 00000-0 12632-3 0 9994
2 68762 97.8537 75.1835 0006258 257.9047 102.1479 15.07847584 28232
COSMOS 2612
1 68762U 26083H 26295.56647921 .00002005 00000-0 13516-3 0 9998
2 68762 97.8537 76.1326 0006327 254.2189 105.8341 15.07851426 28375
COSMOS 2612
1 68762U 26083H 26296.29644999 .00002091 00000-0 14081-3 0 9994
2 68762 97.8538 76.8783 0006385 251.4327 108.6207 15.07854402 28488
COSMOS 2612
1 68762U 26083H 26297.29186266 .00002093 00000-0 14092-3 0 9997
2 68762 97.8545 77.8974 0006462 247.4186 112.6358 15.07858138 28632
COSMOS 2612
1 68762U 26083H 26298.68543625 .00001992 00000-0 13427-3 0 9995
2 68762 97.8551 79.3213 0006547 241.9903 118.0662 15.07863046 28847
COSMOS 2612
1 68762U 26083H 26299.28268042 .00001802 00000-0 12187-3 0 9991
2 68762 97.8556 79.9319 0006489 239.3908 120.6680 15.07864828 28934
COSMOS 2612
1 68762U 26083H 26311.29374648 .00003029 00000-0 20161-3 0 09991
2 68762 97.8560 92.2054 0007271 194.2330 165.8694 15.07910707030747
COSMOS 2612
1 68762U 26083H 26311.95733085 .00003625 00000+0 24040-3 0 9999
2 68762 97.8562 92.8836 0007328 192.0782 168.0271 15.07915641 30842
COSMOS 2612
1 68762U 26083H 26311.95733085 .00003625 00000-0 24040-3 0 9990
2 68762 97.8562 92.8836 0007329 192.0782 168.0271 15.07915641 30843
+138
View File
@@ -0,0 +1,138 @@
COSMOS 2613
1 68763U 26083J 26154.89498458 .00002226 00000+0 15111-3 0 9995
2 68763 97.7787 292.7811 0011357 98.5189 261.7326 15.07430304 7207
COSMOS 2613
1 68763U 26083J 26158.74498038 .00002982 00000+0 20082-3 0 9999
2 68763 97.7785 296.6735 0011128 87.1639 273.0862 15.07449590 7784
COSMOS 2613
1 68763U 26083J 26159.74066109 .00003023 00000+0 20351-3 0 9996
2 68763 97.7785 297.6801 0011082 84.1892 276.0598 15.07454508 7937
COSMOS 2613
1 68763U 26083J 26167.70597507 .00001729 00000+0 11813-3 0 9996
2 68763 97.7805 305.7336 0011083 61.0051 299.2286 15.07493333 9130
COSMOS 2613
1 68763U 26083J 26171.42306266 .00001432 00000+0 98590-4 0 9999
2 68763 97.7804 309.4927 0010780 49.0648 311.1511 15.07503759 9697
COSMOS 2613
1 68763U 26083J 26260.78621025 .00001682 00000-0 12029-3 0 9990
2 68763 97.7944 39.7319 0008396 48.6841 311.5106 15.05837905 23141
COSMOS 2613
1 68763U 26083J 26261.65005557 .00001473 00000-0 10589-3 0 9990
2 68763 97.7944 40.6048 0008362 46.2290 313.9627 15.05839874 23271
COSMOS 2613
1 68763U 26083J 26262.77969735 .00001215 00000-0 88112-4 0 9993
2 68763 97.7945 41.7464 0008308 42.9774 317.2100 15.05842274 23446
COSMOS 2613
1 68763U 26083J 26263.70998881 .00001063 00000-0 77649-4 0 9995
2 68763 97.7945 42.6865 0008252 40.1937 319.9898 15.05844326 23588
COSMOS 2613
1 68763U 26083J 26264.64027928 .00000913 00000-0 67280-4 0 9997
2 68763 97.7945 43.6266 0008192 37.3519 322.8275 15.05846041 23725
COSMOS 2613
1 68763U 26083J 26265.70346696 .00000944 00000-0 69472-4 0 9990
2 68763 97.7946 44.7011 0008120 33.9908 326.1837 15.05848778 23888
COSMOS 2613
1 68763U 26083J 26266.76665336 .00001006 00000-0 73707-4 0 9998
2 68763 97.7947 45.7756 0008037 30.5254 329.6439 15.05851340 24044
COSMOS 2613
1 68763U 26083J 26267.76338870 .00001156 00000-0 84041-4 0 9998
2 68763 97.7949 46.7829 0007949 27.2142 332.9500 15.05854292 24199
COSMOS 2613
1 68763U 26083J 26268.69367350 .00001376 00000-0 99156-4 0 9993
2 68763 97.7950 47.7231 0007862 24.1106 336.0487 15.05857491 24338
COSMOS 2613
1 68763U 26083J 26269.69040564 .00001507 00000-0 10820-3 0 9995
2 68763 97.7952 48.7305 0007763 20.7288 339.4252 15.05860441 24488
COSMOS 2613
1 68763U 26083J 26270.75358484 .00001539 00000-0 11037-3 0 9993
2 68763 97.7954 49.8050 0007658 17.0861 343.0621 15.05863087 24646
COSMOS 2613
1 68763U 26083J 26271.61741635 .00001596 00000-0 11433-3 0 9996
2 68763 97.7956 50.6782 0007578 14.1178 346.0259 15.05865562 24774
COSMOS 2613
1 68763U 26083J 26273.74376433 .00001611 00000-0 11535-3 0 9993
2 68763 97.7960 52.8275 0007381 6.8716 353.2610 15.05871560 25090
COSMOS 2613
1 68763U 26083J 26274.60759023 .00001673 00000-0 11960-3 0 9999
2 68763 97.7962 53.7006 0007295 4.0484 356.0800 15.05874545 25223
COSMOS 2613
1 68763U 26083J 26275.60431045 .00001646 00000-0 11769-3 0 9998
2 68763 97.7964 54.7082 0007198 0.8184 359.3053 15.05877560 25375
COSMOS 2613
1 68763U 26083J 26276.60102819 .00001684 00000-0 12035-3 0 9992
2 68763 97.7966 55.7159 0007103 357.6369 2.4822 15.05881074 25529
COSMOS 2613
1 68763U 26083J 26277.59774344 .00001644 00000-0 11753-3 0 9991
2 68763 97.7969 56.7235 0007008 354.4822 5.6326 15.05884399 25674
COSMOS 2613
1 68763U 26083J 26278.72735052 .00001585 00000-0 11346-3 0 9992
2 68763 97.7971 57.8656 0006904 350.8713 9.2387 15.05888446 25842
COSMOS 2613
1 68763U 26083J 26279.72406058 .00001463 00000-0 10510-3 0 9991
2 68763 97.7974 58.8733 0006817 347.6155 12.4903 15.05891475 25991
COSMOS 2613
1 68763U 26083J 26280.78721545 .00001278 00000-0 92369-4 0 9997
2 68763 97.7976 59.9482 0006721 344.0516 16.0498 15.05894380 26158
COSMOS 2613
1 68763U 26083J 26281.65102690 .00001458 00000-0 10472-3 0 9992
2 68763 97.7977 60.8216 0006657 341.1204 18.9774 15.05898417 26282
COSMOS 2613
1 68763U 26083J 26281.98326154 .00001526 00000-0 10939-3 0 9995
2 68763 97.7978 61.1576 0006632 339.9776 20.1189 15.05900003 26334
COSMOS 2613
1 68763U 26083J 26282.71417713 .00001485 00000-0 10655-3 0 9998
2 68763 97.7979 61.8966 0006576 337.3911 22.7025 15.05902288 26441
COSMOS 2613
1 68763U 26083J 26283.71087862 .00001565 00000-0 11209-3 0 9992
2 68763 97.7980 62.9044 0006505 333.8302 26.2595 15.05905994 26593
COSMOS 2613
1 68763U 26083J 26284.70757623 .00002249 00000-0 15909-3 0 9992
2 68763 97.7982 63.9122 0006329 330.8765 29.2107 15.05912328 26745
COSMOS 2613
1 68763U 26083J 26285.30559424 .00001761 00000-0 12555-3 0 9990
2 68763 97.7982 64.5170 0006303 328.3577 31.7270 15.05913005 26832
COSMOS 2613
1 68763U 26083J 26286.63451875 .00001661 00000-0 11860-3 0 9995
2 68763 97.7983 65.8609 0006251 322.6607 37.4184 15.05918109 27037
COSMOS 2613
1 68763U 26083J 26287.63120986 .00001442 00000-0 10358-3 0 9990
2 68763 97.7982 66.8689 0006234 318.2015 41.8734 15.05920928 27188
COSMOS 2613
1 68763U 26083J 26288.76079212 .00001672 00000-0 11936-3 0 9998
2 68763 97.7982 68.0109 0006261 313.8003 46.2705 15.05924734 27352
COSMOS 2613
1 68763U 26083J 26288.96012904 .00001558 00000+0 11157-3 0 9992
2 68763 97.7981 68.2127 0006235 312.3181 47.7517 15.05924946 27380
COSMOS 2613
1 68763U 26083J 26289.62458747 .00001730 00000-0 12333-3 0 9995
2 68763 97.7980 68.8846 0006236 309.3955 50.6718 15.05927201 27483
COSMOS 2613
1 68763U 26083J 26290.28904463 .00002059 00000-0 14592-3 0 9994
2 68763 97.7980 69.5564 0006247 306.5734 53.4917 15.05930287 27585
COSMOS 2613
1 68763U 26083J 26291.61795593 .00002576 00000-0 18146-3 0 9997
2 68763 97.7980 70.9002 0006242 300.9724 59.0889 15.05936335 27782
COSMOS 2613
1 68763U 26083J 26292.28240978 .00002673 00000-0 18807-3 0 9995
2 68763 97.7980 71.5721 0006237 298.2822 61.7774 15.05939213 27881
COSMOS 2613
1 68763U 26083J 26293.27908662 .00002483 00000-0 17505-3 0 9999
2 68763 97.7987 72.5803 0006148 294.7514 65.3073 15.05944828 28038
COSMOS 2613
1 68763U 26083J 26294.60798485 .00002580 00000-0 18166-3 0 9999
2 68763 97.7991 73.9243 0006097 289.2700 70.7867 15.05951235 28238
COSMOS 2613
1 68763U 26083J 26295.60465466 .00002751 00000-0 19339-3 0 9993
2 68763 97.7989 74.9319 0006088 285.0002 75.0551 15.05956451 28388
COSMOS 2613
1 68763U 26083J 26296.26909865 .00002594 00000-0 18256-3 0 9994
2 68763 97.7991 75.6040 0006038 282.3400 77.7150 15.05959872 28482
COSMOS 2613
1 68763U 26083J 26297.33220613 .00002225 00000-0 15721-3 0 9995
2 68763 97.7996 76.6794 0005955 278.0182 82.0369 15.05964701 28646
COSMOS 2613
1 68763U 26083J 26298.72753094 .00001747 00000-0 12440-3 0 9996
2 68763 97.8002 78.0913 0005908 272.0033 88.0517 15.05967749 28850
COSMOS 2613
1 68763U 26083J 26299.32552597 .00001488 00000-0 10661-3 0 9993
2 68763 97.8002 78.6962 0005892 269.4581 90.5970 15.05969133 28940
+126
View File
@@ -0,0 +1,126 @@
COSMOS 2614
1 68764U 26083K 26159.75792086 .00001829 00000+0 13070-3 0 9990
2 68764 97.7766 296.9035 0007598 55.5546 304.6397 15.05748131 7934
COSMOS 2614
1 68764U 26083K 26260.88806664 .00001417 00000-0 10135-3 0 9998
2 68764 97.7911 38.9656 0007662 68.3607 291.8435 15.06103110 23156
COSMOS 2614
1 68764U 26083K 26261.61888566 .00001431 00000-0 10228-3 0 9990
2 68764 97.7911 39.7041 0007609 66.2476 293.9548 15.06104642 23266
COSMOS 2614
1 68764U 26083K 26262.74833115 .00001492 00000-0 10648-3 0 9998
2 68764 97.7913 40.8455 0007534 63.0211 297.1784 15.06107474 23432
COSMOS 2614
1 68764U 26083K 26263.74489829 .00001596 00000-0 11352-3 0 9996
2 68764 97.7914 41.8526 0007470 60.1746 300.0222 15.06110555 23584
COSMOS 2614
1 68764U 26083K 26264.60858818 .00001665 00000-0 11827-3 0 9994
2 68764 97.7916 42.7255 0007418 57.7445 302.4499 15.06113219 23712
COSMOS 2614
1 68764U 26083K 26265.73802573 .00001876 00000-0 13270-3 0 9994
2 68764 97.7919 43.8669 0007359 54.5898 305.6015 15.06117829 23885
COSMOS 2614
1 68764U 26083K 26266.73458536 .00002006 00000-0 14157-3 0 9992
2 68764 97.7922 44.8741 0007313 51.8326 308.3558 15.06121963 24031
COSMOS 2614
1 68764U 26083K 26267.79757833 .00002127 00000-0 14980-3 0 9999
2 68764 97.7926 45.9485 0007272 48.8657 311.3196 15.06126942 24190
COSMOS 2614
1 68764U 26083K 26268.72769419 .00002203 00000-0 15495-3 0 9993
2 68764 97.7929 46.8887 0007235 46.3317 313.8508 15.06131441 24334
COSMOS 2614
1 68764U 26083K 26269.72424397 .00002092 00000-0 14736-3 0 9990
2 68764 97.7932 47.8960 0007195 43.6529 316.5266 15.06135427 24486
COSMOS 2614
1 68764U 26083K 26270.78722771 .00001798 00000-0 12726-3 0 9994
2 68764 97.7935 48.9706 0007148 40.7283 319.4477 15.06138578 24646
COSMOS 2614
1 68764U 26083K 26271.65090005 .00001596 00000-0 11346-3 0 9992
2 68764 97.7936 49.8437 0007106 38.3118 321.8612 15.06141147 24770
COSMOS 2614
1 68764U 26083K 26273.71042126 .00001025 00000-0 74421-4 0 9993
2 68764 97.7938 51.9257 0006991 32.2542 327.9111 15.06145492 25084
COSMOS 2614
1 68764U 26083K 26274.64052572 .00000903 00000-0 66112-4 0 9994
2 68764 97.7938 52.8661 0006929 29.4007 330.7608 15.06147459 25222
COSMOS 2614
1 68764U 26083K 26275.63706546 .00000779 00000-0 57605-4 0 9999
2 68764 97.7938 53.8735 0006855 26.2631 333.8942 15.06149002 25378
COSMOS 2614
1 68764U 26083K 26276.63360420 .00000793 00000-0 58570-4 0 9995
2 68764 97.7938 54.8811 0006772 23.0480 337.1049 15.06150949 25523
COSMOS 2614
1 68764U 26083K 26277.56370613 .00000838 00000-0 61683-4 0 9992
2 68764 97.7939 55.8214 0006691 19.9562 340.1925 15.06152766 25668
COSMOS 2614
1 68764U 26083K 26278.75954941 .00000975 00000-0 71042-4 0 9998
2 68764 97.7940 57.0304 0006578 15.9086 344.2346 15.06155647 25848
COSMOS 2614
1 68764U 26083K 26279.68964875 .00001088 00000-0 78732-4 0 9997
2 68764 97.7941 57.9708 0006491 12.6802 347.4586 15.06157838 25980
COSMOS 2614
1 68764U 26083K 26280.75261772 .00001184 00000-0 85281-4 0 9997
2 68764 97.7943 59.0456 0006394 8.9266 351.2073 15.06160383 26141
COSMOS 2614
1 68764U 26083K 26281.61627792 .00001614 00000-0 11463-3 0 9999
2 68764 97.7945 59.9189 0006321 5.8811 354.2489 15.06164646 26271
COSMOS 2614
1 68764U 26083K 26281.94845454 .00001727 00000-0 12238-3 0 9998
2 68764 97.7946 60.2547 0006288 4.6926 355.4359 15.06166166 26325
COSMOS 2614
1 68764U 26083K 26282.74567713 .00001920 00000-0 13553-3 0 9992
2 68764 97.7948 61.0608 0006212 1.8797 358.2452 15.06169342 26443
COSMOS 2614
1 68764U 26083K 26283.74220313 .00002220 00000-0 15601-3 0 9995
2 68764 97.7950 62.0685 0006126 358.3927 1.7279 15.06174058 26594
COSMOS 2614
1 68764U 26083K 26284.73872464 .00003394 00000-0 23608-3 0 9994
2 68764 97.7953 63.0758 0006038 354.2947 5.8210 15.06181438 26745
COSMOS 2614
1 68764U 26083K 26285.33663588 .00002938 00000-0 20497-3 0 9992
2 68764 97.7955 63.6805 0005996 352.3742 7.7392 15.06183389 26839
COSMOS 2614
1 68764U 26083K 26286.66532086 .00002813 00000-0 19638-3 0 9994
2 68764 97.7961 65.0244 0005880 348.3702 11.7388 15.06191524 27035
COSMOS 2614
1 68764U 26083K 26287.66183042 .00002453 00000-0 17181-3 0 9990
2 68764 97.7964 66.0323 0005781 345.4457 14.6603 15.06196416 27185
COSMOS 2614
1 68764U 26083K 26288.72477090 .00002195 00000-0 15420-3 0 9994
2 68764 97.7966 67.1075 0005667 342.3215 17.7813 15.06201050 27340
COSMOS 2614
1 68764U 26083K 26288.92407169 .00002230 00000+0 15655-3 0 9995
2 68764 97.7966 67.3090 0005644 341.7157 18.3866 15.06202253 27376
COSMOS 2614
1 68764U 26083K 26289.65484094 .00002125 00000-0 14940-3 0 9998
2 68764 97.7966 68.0482 0005563 339.4496 20.6506 15.06205312 27487
COSMOS 2614
1 68764U 26083K 26290.31917500 .00002186 00000-0 15354-3 0 9993
2 68764 97.7966 68.7201 0005497 337.3105 22.7878 15.06208668 27586
COSMOS 2614
1 68764U 26083K 26291.64783982 .00002164 00000-0 15203-3 0 9994
2 68764 97.7965 70.0640 0005355 332.8273 27.2673 15.06214376 27785
COSMOS 2614
1 68764U 26083K 26292.31217072 .00002061 00000-0 14497-3 0 9991
2 68764 97.7963 70.7359 0005299 330.3334 29.7591 15.06216680 27882
COSMOS 2614
1 68764U 26083K 26293.30866468 .00001850 00000-0 13056-3 0 9995
2 68764 97.7964 71.7438 0005338 325.3729 34.7149 15.06219571 28033
COSMOS 2614
1 68764U 26083K 26294.63732004 .00001699 00000-0 12027-3 0 9998
2 68764 97.7965 73.0877 0005253 320.1245 39.9595 15.06223911 28235
COSMOS 2614
1 68764U 26083K 26295.63380879 .00001522 00000-0 10820-3 0 9992
2 68764 97.7961 74.0956 0005110 316.0960 43.9860 15.06227146 28380
COSMOS 2614
1 68764U 26083K 26296.23170105 .00001454 00000-0 10359-3 0 9999
2 68764 97.7961 74.7003 0005105 313.2849 46.7951 15.06228985 28473
COSMOS 2614
1 68764U 26083K 26297.29461942 .00001283 00000-0 91891-4 0 9999
2 68764 97.7961 75.7755 0005105 308.3285 51.7482 15.06231680 28636
COSMOS 2614
1 68764U 26083K 26298.68969847 .00001285 00000-0 92040-4 0 9999
2 68764 97.7970 77.1870 0005106 302.6161 57.4572 15.06234669 28847
COSMOS 2614
1 68764U 26083K 26299.28758840 .00001215 00000-0 87210-4 0 9991
2 68764 97.7970 77.7918 0005096 299.9236 60.1484 15.06235866 28938
+28
View File
@@ -0,0 +1,28 @@
# TLE/OMM sources (collected 2026-11-08)
Files: `<norad>.tle` (3-line TLEs, sorted by epoch, deduplicated on numeric element content, checksums verified),
`<norad>.omm.json` (full Space-Track OMM records from OMPub/RSO), `index.csv` (epoch, lines, and every source that carried each element set).
All content is untrusted downloaded data; nothing from it was executed.
## Worked
1. **OMPub/RSO "Orbital Witness" daily archive** (main source; one element set per object per day, the latest Space-Track GP_HISTORY row each day).
Ledger: https://raw.githubusercontent.com/OMPub/RSO/node/ledger.json
Bundles: https://github.com/OMPub/RSO/releases/download/rso-archive-YYYY-MM-DD/rso-archive-YYYY-MM-DD.tar.gz (catalog.json.gz inside)
Downloaded 2026-09-18 through 2026-10-27. Source tag in index.csv: `OMPub_RSO:<archive date>`. Found through KeepTrack's public OpenAPI (`/v4/rso/{date}` proxies these releases).
2. **jazzpi/ssa**, `data/cosmos-2612-iceye-x36.csv` (Space-Track OMM CSV with TLE lines; epochs 2026-10-16 to 2026-10-19, several per day, including revised re-issues of the same epoch).
https://raw.githubusercontent.com/jazzpi/ssa/HEAD/data/cosmos-2612-iceye-x36.csv
3. Single snapshots found in other GitHub repos (searched across full git history):
- code-lab-org/tatc `docs/validation/socrates_2026-10-25.tle` (59103, epoch 2026-10-24)
- Atharvchaskar008/EDITH `deploy/showcase/runs/20261009T2252Z/catalog.json`, `addons/a_history/out/catalog_enriched.json` (epochs 2026-10-08/09)
- shishird-03/Orbitguard `data/catalog.tle` (epochs 2026-10-15)
- Out of window (earlier in 2026): Proxy939/AstraShield `data/cache/celestrak_active.tle`, ColaBear101/SattrackSlop `data/catalogue.txt`, Sky-OuO/Pc_TCN `data/collected_tle_datas_2026*.json`, kris70lesgo/helix `coral/data/helix_core/satellites.jsonl`
4. Current element sets only (Nov 7-8 2026, outside the window): n2yo.com satellite pages, orbitalradar.com/satellite/<id>, celestrak gp.php?CATNR=68762, tle.ivanstanojevic.me, db.satnogs.org API.
## Failed / not useful
- web.archive.org: every connection reset through the agent proxy (CDX and snapshots). archive.org/wayback/available reported no snapshots of celestrak gp.php?CATNR=59103/68762 for Oct 2026.
- CelesTrak: serves current data only. Connections were reset some of the time (gp.php?CATNR=59103, last-30-days, supplemental).
- SatNOGS DB `/api/tle/?norad_cat_id=`: latest TLE only, no history. For 68762 it returned an empty list.
- KeepTrack: /satellite/<id> pages are behind a Cloudflare challenge (403). The v4 API needs a key (401), and its OpenAPI has no per-object history endpoint. The v2 API was shut down (410).
- tle.ivanstanojevic.me, n2yo, orbitalradar: latest set only.
- Marco Langbroek blog (sattrackcam.blogspot.com), keeptrack.space deep-dive, integrityisr.com, diplomacyandlaw.com: analysis only, no TLE text.
- zendir-dev/space-range-scripts and DigitalArsenal/space-data-network-modules: derived or illustrative data only (May 2026), or a local archive that is not in the repo.
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Compare the reconstruction with the public element-set history (tle/*.tle)."""
import json, math, sys
from datetime import datetime, timedelta, timezone
import numpy as np
from sgp4.api import Satrec, jday
HERE = __file__.rsplit('/', 1)[0]
MU = 398600.4418
def load(path):
L = [l.rstrip('\n') for l in open(path) if l.strip()]
out = []
for i in range(len(L)):
if L[i].startswith('1 ') and i + 1 < len(L) and L[i + 1].startswith('2 '):
s = Satrec.twoline2rv(L[i], L[i + 1])
ep = datetime(2000, 1, 1, tzinfo=timezone.utc) + timedelta(days=s.jdsatepoch + s.jdsatepochF - 2451545.0 + 0.5)
out.append((ep, s, L[i], L[i + 1]))
out.sort(key=lambda x: x[0])
return out
def st(s, d):
jd, fr = jday(d.year, d.month, d.day, d.hour, d.minute, d.second + d.microsecond / 1e6)
e, r, v = s.sgp4(jd, fr)
return np.array(r), np.array(v)
def rel_rtn(rx, vx, rc):
R = rx / np.linalg.norm(rx)
N = np.cross(rx, vx); N /= np.linalg.norm(N)
T = np.cross(N, R)
th = math.atan2(np.dot(np.cross(rx, rc), N), np.dot(rx, rc))
return np.array([np.linalg.norm(rc) - np.linalg.norm(rx), th * np.linalg.norm(rx), np.dot(rc, N)]) * 1000
def sma(s):
n = s.no_kozai / 60
return (MU / n ** 2) ** (1 / 3)
X = load(f'{HERE}/tle/59103.tle')
C = load(f'{HERE}/tle/68762.tle')
D = json.load(open(f'{HERE}/data.json'))
T0 = datetime.fromisoformat(D['t0'].replace('Z', '+00:00'))
rel = np.array(D['rel']).reshape(-1, 3)
def model_rel(d):
i = (d - T0).total_seconds() / D['dt']
if i < 0 or i >= len(rel) - 1:
return None
k = int(i)
return rel[k] + (rel[k + 1] - rel[k]) * (i - k)
def nearest_x(d):
return min(X, key=lambda x: abs((x[0] - d).total_seconds()))
rows = []
for ep, s, l1, l2 in C:
if ep < datetime(2026, 9, 25, tzinfo=timezone.utc) or ep > datetime(2026, 10, 26, tzinfo=timezone.utc):
continue
xe, xs, _, _ = nearest_x(ep)
rx, vx = st(xs, ep)
rc, vc = st(s, ep)
r = rel_rtn(rx, vx, rc)
m = model_rel(ep)
dA = sma(s) - sma(xs)
di = math.degrees(s.inclo - xs.inclo)
dO = math.degrees(s.nodeo - xs.nodeo)
ec = (s.ecco * math.cos(s.argpo), s.ecco * math.sin(s.argpo))
ex = (xs.ecco * math.cos(xs.argpo), xs.ecco * math.sin(xs.argpo))
rows.append(dict(ep=ep.isoformat()[:16], xage_h=round((ep - xe).total_seconds() / 3600, 1), R=r[0], T=r[1], N=r[2], dA_km=dA, di=di, dO=dO,
dex=(ec[0] - ex[0]) * sma(xs) * 1000, dey=(ec[1] - ex[1]) * sma(xs) * 1000,
mT=None if m is None else m[1], mR=None if m is None else m[0]))
print(f"{'epoch':17} {'xAge':>5} {'TLE T km':>10} {'model T':>10} {'TLE R km':>9} {'mod R':>7} {'N km':>7} {'dA km':>8} {'di deg':>7} {'dRAAN':>7} {'|de| m':>7}")
for r in rows:
print(f"{r['ep']:17} {r['xage_h']:5} {r['T']/1000:10.2f} {('' if r['mT'] is None else f'{r[chr(109)+chr(84)]/1000:10.2f}'):>10} {r['R']/1000:9.2f} {('' if r['mR'] is None else f'{r[chr(109)+chr(82)]/1000:7.2f}'):>7} {r['N']/1000:7.2f} {r['dA_km']:8.3f} {r['di']:7.3f} {r['dO']:7.3f} {math.hypot(r['dex'], r['dey']):7.0f}")
json.dump(rows, open(f'{HERE}/tle_rows.json', 'w'), default=str, indent=0)
+506
View File
@@ -0,0 +1,506 @@
[
{
"ep": "2026-09-25T16:48",
"xage_h": 3.5,
"R": 8301.670539523002,
"T": -8431513.722806757,
"N": -27791.39877508352,
"dA_km": 0.8983573483865257,
"di": 0.018299999999994283,
"dO": 0.3773000000000063,
"dex": -697.9292820779607,
"dey": 9783.360065963709,
"mT": null,
"mR": null
},
{
"ep": "2026-09-26T16:42",
"xage_h": 3.5,
"R": 7570.832860136761,
"T": -8559145.016498953,
"N": -27973.72346945707,
"dA_km": 0.9106827027590043,
"di": 0.017800000000001946,
"dO": 0.3791999999999937,
"dex": -153.69173268221,
"dey": 9872.028733885176,
"mT": null,
"mR": null
},
{
"ep": "2026-09-27T18:12",
"xage_h": 11.5,
"R": 6815.196592958273,
"T": -8698111.802384106,
"N": -28185.292493290643,
"dA_km": 0.9188939955174646,
"di": 0.01769999999998821,
"dO": 0.7201000000000007,
"dex": 404.0796528142847,
"dey": 9785.414288801614,
"mT": null,
"mR": null
},
{
"ep": "2026-09-28T14:54",
"xage_h": 0.3,
"R": 6251.370799312099,
"T": -8811622.590756347,
"N": -28360.676199371275,
"dA_km": 0.9310836171125629,
"di": 0.017600000000012644,
"dO": 0.24769999999999298,
"dex": 1035.6495628865,
"dey": 9792.560732639002,
"mT": null,
"mR": null
},
{
"ep": "2026-09-30T16:22",
"xage_h": 2.0,
"R": 59781.09715962455,
"T": -11167760.90800771,
"N": -27690.064016225777,
"dA_km": 47.460623747911995,
"di": 0.017099999999994866,
"dO": 0.31519999999999515,
"dex": -6664.747931481048,
"dey": -4354.727977192556,
"mT": 75639512.43065125,
"mR": 47012.709780189165
},
{
"ep": "2026-10-01T14:54",
"xage_h": 0.6,
"R": 53198.61601430057,
"T": -17454723.663578935,
"N": -24621.218235828022,
"dA_km": 47.475209599662776,
"di": 0.017200000000008597,
"dO": 0.2364000000000059,
"dex": -6864.688025004593,
"dey": -3957.3136394122175,
"mT": 69369559.69491303,
"mR": 46997.502157282
},
{
"ep": "2026-10-02T15:02",
"xage_h": -5.5,
"R": 47456.624127597934,
"T": 19341422.85974959,
"N": -22997.83211902794,
"dA_km": 47.488684068408475,
"di": 0.017200000000008597,
"dO": -0.04620000000000298,
"dex": -7044.010744269706,
"dey": -3504.5754305638343,
"mT": 62651790.55734729,
"mR": 46983.64728609734
},
{
"ep": "2026-10-03T15:11",
"xage_h": 1.1,
"R": 49113.90241477966,
"T": 12623642.1421986,
"N": -20230.508617234882,
"dA_km": 47.494710533775105,
"di": 0.01730000000000961,
"dO": 0.21320000000000022,
"dex": -7289.578703191674,
"dey": -3123.0249822608152,
"mT": 55934040.62735785,
"mR": 46984.004027369
},
{
"ep": "2026-10-04T15:19",
"xage_h": 1.4,
"R": 51956.892104823964,
"T": 5903878.433825442,
"N": -16608.864333671896,
"dA_km": 47.504150727611886,
"di": 0.017400000000010622,
"dO": 0.201599999999991,
"dex": -7468.8080473937525,
"dey": -2684.489511437165,
"mT": 49216279.297819875,
"mR": 46998.27676768883
},
{
"ep": "2026-10-05T17:04",
"xage_h": 1.6,
"R": 56127.7648462119,
"T": -1272584.9636815456,
"N": -14658.070253380942,
"dA_km": 47.517611120165384,
"di": 0.017799999999989224,
"dO": 0.18929999999999383,
"dex": -7595.534591930674,
"dey": -2300.7489457738766,
"mT": 42050639.05895918,
"mR": 47014.7910767715
},
{
"ep": "2026-10-06T17:12",
"xage_h": 3.5,
"R": 61162.61451995888,
"T": -8001362.706066734,
"N": -12259.211912978088,
"dA_km": 47.527051009651586,
"di": 0.01810000000000498,
"dO": 0.24560000000000357,
"dex": -7732.876100480503,
"dey": -1828.4307699860303,
"mT": 35332833.7000085,
"mR": 47017.20328388
},
{
"ep": "2026-10-07T18:57",
"xage_h": 5.3,
"R": 58306.33261745879,
"T": -15185307.898810113,
"N": -8576.099649933036,
"dA_km": 47.53267538061027,
"di": 0.04850000000000716,
"dO": 0.3031,
"dex": -7856.039095884964,
"dey": -1326.2684424572599,
"mT": 28167197.222816538,
"mR": 47003.41404208034
},
{
"ep": "2026-10-08T15:52",
"xage_h": 2.4,
"R": 52514.75479914916,
"T": -21033724.175330356,
"N": -7142.919170412297,
"dA_km": 47.54487423568935,
"di": 0.048499999999994436,
"dO": 0.16079999999999814,
"dex": -7875.484256176535,
"dey": -875.4380866070594,
"mT": 22345153.534087613,
"mR": 46988.85800007833
},
{
"ep": "2026-10-08T22:18",
"xage_h": 2.4,
"R": 51497.189675082154,
"T": 20689322.520410504,
"N": -6897.771094663387,
"dA_km": 47.54761663741556,
"di": 0.048400000000006146,
"dO": 0.15880000000000336,
"dex": -7888.8449969662925,
"dey": -736.3583175980484,
"mT": 20553761.926370453,
"mR": 46985.514538949996
},
{
"ep": "2026-10-09T17:37",
"xage_h": 13.8,
"R": 51446.268358212365,
"T": 15308183.123688515,
"N": -5564.4826493221235,
"dA_km": 47.54331188363631,
"di": 0.048499999999994436,
"dO": 0.6269000000000058,
"dex": -8016.165683777107,
"dey": -402.45493298203405,
"mT": 15179597.556924822,
"mR": 46981.85561155733
},
{
"ep": "2026-10-10T17:45",
"xage_h": 2.9,
"R": 53263.35654009927,
"T": 8580389.659196287,
"N": -2318.666088893505,
"dA_km": 47.56909692627778,
"di": 0.04820000000000413,
"dO": 0.14469999999998795,
"dex": -7921.534256572002,
"dey": 143.57808612758384,
"mT": 8461897.028362235,
"mR": 46991.616345465336
},
{
"ep": "2026-10-11T17:53",
"xage_h": 3.1,
"R": 54550.0623332573,
"T": 1846837.5125594211,
"N": -193.04127280518975,
"dA_km": 47.595951177626375,
"di": 0.048000000000002103,
"dO": 0.13699999999999912,
"dex": -7899.280713350153,
"dey": 649.4787396541252,
"mT": 1744195.6615911566,
"mR": 47008.70145931934
},
{
"ep": "2026-10-11T22:43",
"xage_h": 7.9,
"R": 55217.78072005691,
"T": 498276.48568582215,
"N": 14.719970954402454,
"dA_km": 47.596673418797764,
"di": 0.04810000000000311,
"dO": 0.3386999999999784,
"dex": -7938.713847787283,
"dey": 727.9745830575645,
"mT": 400648.5541642541,
"mR": 47011.6456086155
},
{
"ep": "2026-10-13T14:34",
"xage_h": 0.0,
"R": -379.3799917648357,
"T": -465249.99398902856,
"N": 155.68420920650658,
"dA_km": -0.20016123426285048,
"di": -0.0007999999999953703,
"dO": -0.001300000000000428,
"dex": 476.58445577466443,
"dey": 146.51391943944606,
"mT": -441405.656846914,
"mR": -137.67734491649946
},
{
"ep": "2026-10-14T16:03",
"xage_h": 9.6,
"R": -370.6623293664961,
"T": -435616.048035603,
"N": 161.9253376635085,
"dA_km": -0.20148191778662294,
"di": -0.0007000000000070809,
"dO": 0.40530000000000327,
"dex": 382.3378309829219,
"dey": 124.29206463422811,
"mT": -411226.797806984,
"mR": -137.69767118799967
},
{
"ep": "2026-10-15T17:32",
"xage_h": 1.6,
"R": -2503.896276883097,
"T": -192460.3169489883,
"N": -301.68892792708317,
"dA_km": -2.392243099448933,
"di": -0.0002000000000020231,
"dO": 0.07029999999999868,
"dex": 225.28681157558015,
"dey": -2185.570947233477,
"mT": -186092.5266262922,
"mR": -2435.684305768334
},
{
"ep": "2026-10-15T23:54",
"xage_h": 1.6,
"R": -2520.7756466024875,
"T": -102137.59140317388,
"N": -361.49078247572623,
"dA_km": -2.3967268360192975,
"di": 0.00030000000000303466,
"dO": 0.07080000000000373,
"dex": 180.5525690268643,
"dey": -2130.256216241443,
"mT": -91829.42227460886,
"mR": -2435.787483158333
},
{
"ep": "2026-10-16T14:14",
"xage_h": -6.4,
"R": 583.0853823463258,
"T": -3270.557846359225,
"N": -433.39312076937335,
"dA_km": -0.08511771647772548,
"di": 0.0004000000000040462,
"dO": -0.2674999999999961,
"dex": -624.1237466424434,
"dey": 13.90747080039349,
"mT": -1951.5043704634477,
"mR": -566.8429822244725
},
{
"ep": "2026-10-16T19:00",
"xage_h": -1.6,
"R": 783.0003643284726,
"T": -3185.0422923992455,
"N": -464.5619337225683,
"dA_km": -0.011322251530145877,
"di": 0.0006999999999943587,
"dO": -0.06390000000001027,
"dex": -781.7527486226974,
"dey": 13.879317152998238,
"mT": -1793.30056536121,
"mR": -566.9045184706075
},
{
"ep": "2026-10-17T07:45",
"xage_h": 0.0,
"R": -36.06833963021927,
"T": -2062.7231150018165,
"N": -131.69286971465834,
"dA_km": -0.019333387072038022,
"di": -0.00010000000000101155,
"dO": 0.0011000000000111272,
"dex": 18.03735131556586,
"dey": -6.604478707308161,
"mT": -1372.2502944852936,
"mR": -566.865747285313
},
{
"ep": "2026-10-17T17:18",
"xage_h": -3.2,
"R": 4.599611484081834,
"T": -844.7988259747882,
"N": 52.9706873529662,
"dA_km": -0.008262108925919165,
"di": 0.0002000000000020231,
"dO": -0.13600000000000173,
"dex": 8.20241659340391,
"dey": -42.37747499780447,
"mT": -1056.1695658493434,
"mR": -567.0426313209194
},
{
"ep": "2026-10-18T07:38",
"xage_h": 0.0,
"R": 152.94882451416925,
"T": -5054.667467974843,
"N": -0.0015860543004973268,
"dA_km": 0.14360808967558114,
"di": -0.00010000000001373377,
"dO": 0.0,
"dex": -6.1507886417584405,
"dey": -24.215847918516292,
"mT": -5048.3488581106485,
"mR": -309.2383605079865
},
{
"ep": "2026-10-18T12:25",
"xage_h": 0.0,
"R": 166.4897103273688,
"T": -9141.165631446744,
"N": 11.970493629610738,
"dA_km": 0.1460070334496777,
"di": -0.00010000000001373377,
"dO": -0.00010000000000101155,
"dex": -14.68690078801386,
"dey": -19.71195752297889,
"mT": -8607.886910045327,
"mR": -308.84969100266164
},
{
"ep": "2026-10-18T23:34",
"xage_h": 1.6,
"R": 524.4227639013843,
"T": -173453.73135471187,
"N": 117.0080639040351,
"dA_km": 2.499636333592207,
"di": 0.00010000000001373377,
"dO": 0.06680000000000144,
"dex": 2075.262986735747,
"dey": 574.0477987188896,
"mT": -169649.0269195732,
"mR": 2103.8094298719734
},
{
"ep": "2026-10-19T07:32",
"xage_h": 0.0,
"R": 450.93551363061124,
"T": -291684.47012140456,
"N": 59.83498641613323,
"dA_km": 2.5002181625650337,
"di": 0.0,
"dO": -0.0004999999999923356,
"dex": 2239.136832296919,
"dey": 653.8710657306938,
"mT": -288802.92716569477,
"mR": 2113.1023277569593
},
{
"ep": "2026-10-20T07:26",
"xage_h": -3.2,
"R": -248.29427269560256,
"T": -478274.52944777923,
"N": 113.74612285833368,
"dA_km": -0.01352205154580588,
"di": 0.0004000000000040462,
"dO": -0.13649999999999404,
"dex": 579.6990452869835,
"dey": -1353.3356906949068,
"mT": -471798.60920187604,
"mR": -384.0763980043663
},
{
"ep": "2026-10-21T15:17",
"xage_h": -4.8,
"R": -106.56981143984012,
"T": -475547.58343992,
"N": 105.74999393116944,
"dA_km": -0.00243878409582976,
"di": 0.0003000000000157569,
"dO": -0.2041999999999919,
"dex": 459.9848709505605,
"dey": -1304.92577357932,
"mT": -474931.43770828,
"mR": -384.41977726933646
},
{
"ep": "2026-10-22T13:35",
"xage_h": -6.4,
"R": -25.612278194785176,
"T": -475275.7919380849,
"N": 96.85685953749777,
"dA_km": 0.00871163720421464,
"di": 0.0003000000000157569,
"dO": -0.2719000000000024,
"dex": 401.45129456279716,
"dey": -1314.8371707017855,
"mT": null,
"mR": null
},
{
"ep": "2026-10-23T07:06",
"xage_h": 11.2,
"R": 79.06994805762224,
"T": -476056.157720825,
"N": 73.51348049365886,
"dA_km": -0.00039473106971854577,
"di": 0.00040000000001676845,
"dO": 0.4737999999999965,
"dex": 185.24785242772384,
"dey": -1290.0198369590855,
"mT": null,
"mR": null
},
{
"ep": "2026-10-24T07:00",
"xage_h": 1.6,
"R": 182.63009205929848,
"T": -478012.9950510453,
"N": -182.2959878778244,
"dA_km": 0.01660918457491789,
"di": 0.0007999999999953703,
"dO": 0.06929999999998857,
"dex": 162.54377588451672,
"dey": -1311.8927366875912,
"mT": null,
"mR": null
},
{
"ep": "2026-10-25T16:27",
"xage_h": 0.0,
"R": 310.18787481571053,
"T": -482647.12777369755,
"N": -179.45165744213804,
"dA_km": 0.027587932723690756,
"di": 0.0005999999999933472,
"dO": 0.001499999999989729,
"dex": 70.84036878505839,
"dey": -1347.0923526858505,
"mT": null,
"mR": null
}
]