天体の位置を計算するための方法が、海上保安庁から提供されています。
計算してみましょう!!
今回は面倒なので太陽だけですが、太陽、金星、火星、木星、土星、月、といった、代表的な天体が含まれています。
天体の計算が、なぜ国立天文台ではなく、海上保安庁なのか?
GPSなんてものがなかった時代、広い海の上で自分の居場所を知るには、天測航法に頼る必要がありました。そのため、各国の海軍やそれに相当する機関が天文観測に注力していました。
測位は現在でこそGPS等の衛星ベースの技術にその座を奪われ、その運用も米では宇宙軍が、日本では内閣府が行っていますが、かつての航法システム、例えばオメガやロランといったシステムは、各国の海軍や沿岸警備隊、日本では海上保安庁といった、海に関わる機関が運用していました。
ソースコード
classCelestialPositionCalculation{publicenumType{SunRa,SunDec,SunAu}publicstructData{publicintYear{get;set;}publicinta{get;set;}publicintb{get;set;}publicdouble[]Value{get;set;}publicData(intYear,inta,intb,double[]Value){this.Year=Year;this.a=a;this.b=b;this.Value=Value;}}privatestaticreadonlyReadOnlyDictionary<Type,Data[]>dataDic=generateValuesetDic();privatestaticreadonlyReadOnlyDictionary<int,int>deltaTDic=generateDeltaTdic();publicstaticdoubleCalc(DateTimeOffsetDate,Typetitle){doublet=CalcT(Date);Datapage;if(!searchPage(title,Date,t,outpage)){return(double.NaN);}inta=page.a;intb=page.b;doubletheta=Math.Acos((2.0*t-(a+b))/(b-a));doublef=page.Value.Select((tmp,i)=>tmp*Math.Cos(i*theta)).Sum();return(f);}publicstaticdoubleCalcT(DateTimeOffsetDate){DateTimeUTC=Date.UtcDateTime;intP=UTC.Month-1;intQ=(int)Math.Floor((UTC.Month+7)/10.0);intY=(int)Math.Floor(UTC.Year/4.0-Math.Floor(UTC.Year/4.0)+0.77);intS=(int)Math.Floor(P*0.55-0.33);intT=30*P+Q*(S-Y)+P*(1-Q)+UTC.Day;doubleF=UTC.Hour/24.0+UTC.Minute/1440.0+UTC.Second/86400.0;doublet=T+F+deltaTDic[UTC.Year]/86400.0;return(t);}privatestaticboolsearchPage(Typetitle,DateTimeOffsetDate,doublet,outDatapage){if(dataDic.ContainsKey(title)){vartmp=dataDic[title].Where(a=>a.Year==Date.UtcDateTime.Year&&a.a<=t&&t<=a.b);if(tmp.Any()){page=tmp.First();return(true);}}page=newData();return(false);}privatestaticReadOnlyDictionary<Type,Data[]>generateValuesetDic(){return(newReadOnlyDictionary<Type,Data[]>(newDictionary<Type,Data[]>(){{Type.SunRa,newData[]{newData(2020,0,122,new[]{22.71187,3.929287,-0.104071,0.035631,0.006885,-0.002385,0.000138,0.00005,-0.000056,0.000014,-0.000054,-0.000018,0.000062,0.000008,-0.000027,-0.000002,0.000003,0,}),newData(2020,121,245,new[]{6.6489,4.134433,-0.04333,-0.04038,0.005603,0.003175,-0.000428,-0.000194,0.000005,0.000005,-0.000046,0.000041,0.000056,-0.000028,-0.000029,0.00001,0.000007,-0.000004,}),newData(2020,244,367,new[]{14.575618,4.056804,0.152148,0.012255,-0.01366,-0.002129,0.000302,0.000116,0.000024,-0.000018,0,0.000069,0.000002,-0.000045,-0.000002,0.000014,-0.000001,-0.000005,}),}},{Type.SunDec,newData[]{newData(2020,0,122,new[]{-5.75369,20.14227,1.7549,-1.00916,0.01301,0.00848,-0.00361,0.00073,0.00003,-0.00011,-0.00025,-0.00014,0.00027,0.00016,-0.00012,-0.00008,0.00001,0.00002,}),newData(2020,121,245,new[]{17.14314,-3.5435,-5.77996,0.22272,0.1592,-0.01133,-0.00458,0.00086,0.00031,0.00019,-0.00009,-0.00001,-0.00003,-0.00011,0.00004,0.00007,-0.00001,-0.00004,}),newData(2020,244,367,new[]{-10.75539,-16.72221,3.56387,0.97158,-0.02871,-0.02364,-0.00538,0.00036,0.00024,0.00008,0.00011,-0.00034,-0.00003,0.00028,-0.00001,-0.00012,0.00001,0.00004,}),}},{Type.SunAu,newData[]{newData(2020,0,122,new[]{0.993193,0.012806,0.002311,-0.000662,-0.000048,0.000004,0.000002,-0.000014,-0.000002,-0.000004,-0.000004,0.000017,0.000004,-0.000011,-0.000002,0.000004,0.000001,-0.000001,}),newData(2020,121,245,new[]{1.012388,0.000983,-0.004213,-0.000046,0.000088,-0.000009,-0.000003,-0.000012,0.000006,-0.000001,0.000007,0.000015,-0.000009,-0.000011,0.000004,0.000004,-0.000002,-0.000001,}),newData(2020,244,367,new[]{0.994543,-0.013788,0.00185,0.000719,-0.000052,-0.000012,-0.000006,0,0.000009,0,0.000014,0,-0.000017,0,0.000008,0,-0.000002,0,}),}},}));}privatestaticReadOnlyDictionary<int,int>generateDeltaTdic(){return(newReadOnlyDictionary<int,int>(newDictionary<int,int>(){{2020,70}}));}}定数をソースコードに埋め込んでいるため、そのあたりが複雑ですが、計算自体は結構簡単です。
使用例
Chartでグラフ化のサンプル
Fontfont=newFont("",20,FontStyle.Bold,GraphicsUnit.Pixel);DateTimeOffsetstart=newDateTimeOffset(newDateTime(2020,1,1),newTimeSpan());DateTimeOffsetend=newDateTimeOffset(newDateTime(2021,1,1),newTimeSpan());Chartchart=newChart();chart.Size=newSize(1920,1080);ChartAreaarea=newChartArea();chart.ChartAreas.Add(area);Legendlegend=newLegend();chart.Legends.Add(legend);legend.Docking=Docking.Top;Seriesmarker=newSeries();chart.Series.Add(marker);marker.IsVisibleInLegend=false;marker.ChartType=SeriesChartType.Point;marker.MarkerSize=20;marker.MarkerStyle=MarkerStyle.Circle;marker.Font=font;marker.LabelBackColor=Color.LightGray;Seriesra=newSeries("R.A. [hour]");chart.Series.Add(ra);ra.ChartType=SeriesChartType.Line;ra.BorderWidth=3;Seriesdec=newSeries("Dec. [deg]");chart.Series.Add(dec);dec.ChartType=SeriesChartType.Line;dec.BorderWidth=3;Seriesau=newSeries("Dist. [AU]");chart.Series.Add(au);au.YAxisType=AxisType.Secondary;au.ChartType=SeriesChartType.Line;au.BorderWidth=3;area.AxisX.Minimum=start.UtcDateTime.ToOADate();area.AxisX.Maximum=end.UtcDateTime.ToOADate();area.AxisX.Title="UTC";area.AxisX.TitleFont=font;area.AxisX.LabelStyle.Format="yyyy/MM";area.AxisX.IntervalType=DateTimeIntervalType.Months;area.AxisX.Interval=1;area.AxisX.MinorGrid.Enabled=true;area.AxisX.MinorGrid.IntervalType=DateTimeIntervalType.Weeks;area.AxisX.MinorGrid.Interval=1;area.AxisX.MinorGrid.LineColor=Color.LightGray;area.AxisY.Title="[hour], [deg]";area.AxisY.TitleFont=font;area.AxisY.Minimum=-25;area.AxisY.Maximum=+25;area.AxisY.Interval=5;area.AxisY.MinorGrid.Enabled=true;area.AxisY.MinorGrid.Interval=1;area.AxisY.MinorGrid.LineColor=Color.LightGray;area.AxisY2.Title="[AU]";area.AxisY2.TitleFont=font;area.AxisY2.Minimum=0.98;area.AxisY2.Maximum=1.03;area.AxisY2.Interval=0.005;for(DateTimeOffsetdate=start;date<end;date+=newTimeSpan(1,0,0)){doubleoad=date.UtcDateTime.ToOADate();ra.Points.AddXY(oad,CelestialPositionCalculation.Calc(date,CelestialPositionCalculation.Type.SunRa)%24);dec.Points.AddXY(oad,CelestialPositionCalculation.Calc(date,CelestialPositionCalculation.Type.SunDec));au.Points.AddXY(oad,CelestialPositionCalculation.Calc(date,CelestialPositionCalculation.Type.SunAu));}{DateTimeOffsetdate;date=newDateTimeOffset(newDateTime(2020,3,20,12,50,0));marker.Points.Add(newDataPoint(date.UtcDateTime.ToOADate(),CelestialPositionCalculation.Calc(date,CelestialPositionCalculation.Type.SunDec)){Label="春分の日",});date=newDateTimeOffset(newDateTime(2020,6,21,6,44,0));marker.Points.Add(newDataPoint(date.UtcDateTime.ToOADate(),CelestialPositionCalculation.Calc(date,CelestialPositionCalculation.Type.SunDec)){Label="夏至",});date=newDateTimeOffset(newDateTime(2020,9,22,22,31,0));marker.Points.Add(newDataPoint(date.UtcDateTime.ToOADate(),CelestialPositionCalculation.Calc(date,CelestialPositionCalculation.Type.SunDec)){Label="秋分の日",});date=newDateTimeOffset(newDateTime(2020,12,21,19,2,0));marker.Points.Add(newDataPoint(date.UtcDateTime.ToOADate(),CelestialPositionCalculation.Calc(date,CelestialPositionCalculation.Type.SunDec)){Label="冬至",});}Bitmapbmp=newBitmap(chart.Width,chart.Height);chart.DrawToBitmap(bmp,newRectangle(){Size=bmp.Size});bmp.Save("log.png");
太陽の赤緯がゼロを通過してプラスに変わったところが春分、そのときの太陽の方向が赤経ゼロで、その日が春分の日です。同様に、赤緯がマイナスに変わる日が秋分の日です。
太陽の赤緯が最大になる日が夏至、最小になる日が冬至です。
また、太陽と地球の距離は、1月上旬を最小とし、7月上旬を最大としています。
太陽から離れれば離れるほど、エネルギーは小さくなりますから、エネルギー的に言えば、実は夏より冬のほうが、太陽から受け取るエネルギーは大きいのです(エネルギーの比でおよそ7%程度)。そのため、人工衛星の太陽電池の能力は、夏至(ほぼ太陽から一番遠い時期)の発電量で表されていたりします。
その他
ちょうど1年前にも同じようなエントリ書いてるんだな…… 書いてから気がついた。。。
