天体の位置を計算してみた

天体の位置を計算するパラメーターは、海上保安庁の海洋情報部から提供されている。
~~世界が世界ならブルマーが提供してくれるはず~~

海上保安庁が出している情報だけあって、目視観測が目的になっている。そのため、太陽、月、金星、火星、木星、土星、といった、観測しやすい天体が記載されている。

[天文・暦情報](https://www1.kaiho.mlit.go.jp/KOHO/)

下の方の"コンピュータによる天体の位置計算式 (天測暦/天測略暦付録)"に計算方法のPDFや数値が書かれたTXTのリンクが有る。
平成21年から31年までの範囲のデータが有る。年末が近くなると翌年分のデータが提供される。

とりあえず手っ取り早く動作確認したかったので、値をソースコードに埋め込んでいる。
データのコピペが面倒だったので太陽と金星と火星のみ扱っている。

![2019-01-04_19-25.png](https://qiita-image-store.s3.amazonaws.com/0/264452/67dbe2c3-1715-e433-578e-fc02e3beae1f.png)

エクセルでグラフ化してみた。
各軸の符号は気にしないように。。。

計算結果は地球を中心とした極座標系で得られるので、直交座標系に変換し、惑星の位置は太陽との差を取る。
また、この図では$x = \sin(ra \div 12 \times \pi) \cos(dec \div 180 \times \pi) dist$のように計算しているが、これは地球の極軸の延長線上から見た形なので、公転面に対する自転軸の傾斜分、つぶれた形になっている。

```c#:C#
static void Main(string[] args)
{
    string[] names = new[] { "Sun", "Venus", "Mars", };

    DateTimeOffset start = new DateTimeOffset(2019, 1, 1, 0, 0, 0, new TimeSpan());
    DateTimeOffset end = new DateTimeOffset(2020, 1, 1, 0, 0, 0, new TimeSpan());

    StringBuilder sb = new StringBuilder();

    foreach (string name in names)
    {
        sb.Append("," + name + " ra");
        sb.Append("," + name + " dec");
        sb.Append("," + name + " dist");
    }
    sb.AppendLine();

    for (int days = 0; start.AddDays(days) < end; days++)
    {
        DateTimeOffset date = start.AddDays(days);

        sb.Append(date);

        foreach (string name in names)
        {
            double ra, dec, dist;
            if (!tryCalcCelestialPosition(name, date, out ra, out dec, out dist))
            {
                ra = dec = dist = double.NaN;
            }

            sb.Append("," + ra);
            sb.Append("," + dec);
            sb.Append("," + dist);
        }

        sb.AppendLine();
    }

    using (StreamWriter sw = new StreamWriter("./log.txt"))
    {
        sw.Write(sb.ToString().Replace(',', '\t'));
    }
}

struct DataSet
{
    public int a { get; private set; }
    public int b { get; private set; }
    public double[] RA { get; private set; }
    public double[] Dec { get; private set; }
    public double[] Dist { get; private set; }

    public DataSet(int a, int b, double[] RA, double[] Dec, double[] Dist)
    {
        if (a >= b ||
            RA.Length != Dec.Length ||
            RA.Length != Dist.Length)
        {
            throw new ArgumentException();
        }

        this.a = a;
        this.b = b;
        this.RA = RA;
        this.Dec = Dec;
        this.Dist = Dist;
    }
}

static bool tryCalcCelestialPosition(string name, DateTimeOffset date, out double RaHour, out double DecDeg, out double DistAu)
{
    RaHour = DecDeg = DistAu = 0;

    string key = name + date.UtcDateTime.Year;

    if (!dict1.ContainsKey(key))
    {
        return (false);
    }

    double t = (date.UtcDateTime - new DateTime(date.UtcDateTime.Year, 1, 1)).TotalDays + 1
        + dict2[date.UtcDateTime.Year] / 86400.0;

    foreach (DataSet set in dict1[key])
    {
        if (set.a > t || t > set.b)
        {
            continue;
        }

        double theta = Math.Acos((2 * t - (set.b + set.a)) / (set.b - set.a));

        for (int i = 0; i < set.RA.Length; i++)
        {
            double cosNtheta = Math.Cos(theta * i);

            RaHour += set.RA[i] * cosNtheta;
            DecDeg += set.Dec[i] * cosNtheta;
            DistAu += set.Dist[i] * cosNtheta;
        }

        return (true);
    }

    return (false);
}

static readonly Dictionary<int, int> dict2 = new Dictionary<int, int>()
{
    { 2019, 70 },
};

static readonly Dictionary<string, DataSet[]> dict1 = new Dictionary<string, DataSet[]>()
{
    { "Sun2019", new DataSet[] {
            new DataSet(0, 121,
                #region
                new [] {
                    22.6964,
                    3.896948, -0.103656, 0.034727, 0.006648, -0.002283,
                    0.000073, 0.000064, -0.000002, 0.000012, 0.000053,
                    -0.000038, -0.000053, 0.000023, 0.000021, -0.000007,
                    -0.000003, 0.000002,
                },
                new []
                {
                    -5.85509,
                    19.99447, 1.75027, -0.98562, 0.01226, 0.008,
                    -0.00368, 0.00067, 0.00016, 0.00014, 0.00022,
                    -0.00011, -0.00028, 0.00002, 0.00014, 0,
                    -0.00003, -0.00001,
                },
                new []
                {
                    0.993121,
                    0.012666, 0.00229, -0.000646, -0.000043, 0.000016,
                    0.000005, 0.000013, -0.000004, 0.000005, -0.000008,
                    -0.000015, 0.000007, 0.000009, -0.000003, -0.000003,
                    0.000001, 0.000001,
                }),
                #endregion
            new DataSet(120, 244,
                #region
                new []
                {
                    6.599667,
                    4.13595, -0.040841, -0.040829, 0.005193, 0.003291,
                    -0.000422, -0.000118, 0.000049, 0.000017, 0.000012,
                    -0.000071, -0.000019, 0.00005, 0.00001, -0.000018,
                    -0.000007, 0.000006,
                },
                new []
                {
                    17.17705,
                    -3.27497, -5.7942, 0.2083, 0.1602, -0.01044,
                    -0.00503, 0.00073, 0.00012, -0.00019, 0.00016,
                    0.00002, 0.00004, 0.00006, -0.00007, -0.00004,
                    0.00002, 0.00003,
                },
                new []
                {
                    1.012388,
                    0.001199, -0.004187, -0.000056, 0.000106, 0.000004,
                    0.000004, 0.000005, -0.00001, 0.000001, -0.000012,
                    -0.000006, 0.000016, 0.000004, -0.000008, -0.000001,
                    0.000002, 0,
                }),
                #endregion
            new DataSet(243, 366,
                #region
                new []
                {
                    14.525213,
                    4.050617, 0.151517, 0.013548, -0.013395, -0.002113,
                    0.000297, 0.000215, 0.000008, 0.000002, -0.00004,
                    -0.000056, 0.000037, 0.000037, -0.000016, -0.000012,
                    0.000004, 0.000004,
                },
                new []
                {
                    -10.58204,
                    -16.89248, 3.49425, 0.97511, -0.02577, -0.02312,
                    -0.00523, -0.00008, 0.00042, -0.00005, 0.00004,
                    0.00029, -0.00017, -0.00022, 0.00011, 0.00009,
                    -0.00003, -0.00002,
                },
                new []
                {
                    0.994695,
                    -0.013897, 0.001825, 0.000724, -0.000027, -0.000017,
                    0.000005, -0.000009, -0.000007, -0.000002, -0.000011,
                    0.000011, 0.000013, -0.000007, -0.000006, 0.000003,
                    0.000001, -0.000001,
                }),
        #endregion
        }
    },
    { "Venus2019", new DataSet[] {
            new DataSet(0, 121,
                #region
                new []
                {
                    20.0894,
                    4.770818, -0.004287, -0.070212, 0.021819, 0.004282,
                    -0.001415, -0.000295, 0.000167, 0.000029, 0.000015,
                    -0.000067, -0.000014, 0.000039, 0.000002, -0.000012,
                    0.000002, 0.000004,
                },
                new []
                {
                    -12.22805,
                    9.61427, 6.78204, -0.4148, -0.39134, 0.05256,
                    0.00901, -0.00365, -0.00038, 0.00048, -0.00006,
                    -0.00008, -0.00013, 0.00001, 0.0001, -0.00001,
                    -0.00002, 0.00001,
                },
                new []
                {
                    1.054744,
                    0.411713, -0.018687, -0.002435, 0.000187, -0.0001,
                    0.000025, 0.000004, -0.000004, 0.000002, -0.000016,
                    -0.000006, 0.000015, 0.000003, -0.000006, -0.000001,
                    0.000001, 0.000001,
                }),
                #endregion
            new DataSet(120, 244,
                #region
                new []
                {
                    5.827986,
                    5.202986, 0.049994, -0.078962, -0.010601, 0.009797,
                    0.001418, -0.000796, -0.000175, 0.000089, 0.000027,
                    -0.000053, -0.000006, 0.000028, 0.000002, -0.000009,
                    -0.000003, 0.000003,
                },
                new []
                {
                    13.89664,
                    2.88981, -8.86463, -0.43301, 0.42723, 0.03293,
                    -0.02073, -0.00349, 0.00152, 0.00036, -0.00001,
                    -0.00009, 0.00005, 0.00006, -0.00006, -0.00002,
                    0.00001, 0.00002,
                },
                new []
                {
                    1.630704,
                    0.141151, -0.051406, -0.001594, 0.000519, 0.000048,
                    -0.000002, 0, -0.000008, 0.000001, -0.000015,
                    -0.000002, 0.000016, 0.000001, -0.000007, 0,
                    0.000002, 0,

                }),
                #endregion
            new DataSet(243, 366,
                #region
                new []
                {
                    15.923639,
                    5.14974, 0.151701, -0.031983, -0.034715, 0.000704,
                    0.003079, 0.000513, -0.000191, -0.000079, -0.000014,
                    -0.000036, 0.000012, 0.000026, -0.000003, -0.000008,
                    0, 0.000003,
                },
                new []
                {
                    -12.22232,
                    -14.72841, 7.56495, 1.4717, -0.27205, -0.08298,
                    -0.00353, 0.00611, 0.00149, -0.00039, -0.00024,
                    0.00013, -0.00005, -0.0001, 0.00005, 0.00005,
                    -0.00001, -0.00001,
                },
                new []
                {
                    1.533443,
                    -0.223213, -0.033985, 0.001937, -0.000248, -0.000017,
                    0.000017, -0.000005, -0.000006, -0.000002, -0.000015,
                    0.000006, 0.000015, -0.000003, -0.000006, 0.000001,
                    0.000001, 0,
                }),
                #endregion
        }
    },
    { "Mars2019", new DataSet[] {
            new DataSet(0, 121,
                #region
                new []
                {
                    2.564142,
                    2.658989, 0.06083, 0.000734, -0.002224, -0.000314,
                    0.000041, -0.00001, 0.000022, -0.000005, 0.000024,
                    0.000016, -0.000032, -0.000011, 0.000016, 0.000004,
                    -0.000003, -0.000001,
                },
                new []
                {
                    13.61414,
                    12.62843, -1.85287, -0.26484, 0.01618, 0.00222,
                    0.00026, -0.00013, 0.0001, -0.00006, 0.00006,
                    0.00013, -0.00013, -0.00008, 0.00008, 0.00002,
                    -0.00003, -0.00001,
                },
                new []
                {
                    1.75839,
                    0.497599, -0.012599, -0.004394, 0.000046, 0.000033,
                    -0.000005, 0.000012, 0.000005, 0.000001, 0.000007,
                    -0.000015, -0.000009, 0.000011, 0.000004, -0.000004,
                    -0.000001, 0.000001,
                }),
                #endregion
            new DataSet(120, 244,
                #region
                new []
                {
                    8.045234,
                    2.747163, -0.071382, -0.004923, 0.003287, 0.000004,
                    -0.000082, 0.000024, 0.000016, -0.000005, 0.000011,
                    -0.000023, -0.000017, 0.000018, 0.000009, -0.000007,
                    -0.000005, 0.000003,
                },
                new []
                {
                    19.04418,
                    -7.60969, -2.37057, 0.21373, 0.01609, -0.00385,
                    0.00021, 0.00003, -0.00004, -0.00005, 0.00002,
                    0.00007, 0.00004, -0.00001, -0.00003, 0,
                    0.00001, 0,
                },
                new []
                {
                    2.508586,
                    0.223481, -0.055137, -0.002149, 0.000199, 0.000011,
                    0.000001, 0.000008, -0.00001, -0.000001, -0.000007,
                    -0.000011, 0.000014, 0.000009, -0.000008, -0.000004,
                    0.000002, 0.000001,
                }),
                #endregion
            new DataSet(243, 366,
                #region
                new []
                {
                    13.162022,
                    2.519937, 0.052854, 0.014762, -0.000897, -0.000253,
                    -0.000019, 0.000007, -0.000014, -0.000002, -0.000015,
                    -0.000015, 0.000022, 0.000011, -0.000011, -0.000003,
                    0.000002, 0.000002,
                },
                new []
                {
                    -5.57105,
                    -14.76437, 0.60721, 0.27608, 0.0074, 0.00011,
                    -0.00031, -0.00011, 0.00006, 0.00001, 0.00007,
                    0.00011, -0.00013, -0.00008, 0.00006, 0.00004,
                    -0.00001, -0.00001,
                },
                new []
                {
                    2.485056,
                    -0.247831, -0.055639, 0.002496, 0.000414, -0.000007,
                    -0.000002, -0.000012, -0.000006, 0.000001, -0.000006,
                    0.000015, 0.00001, -0.000012, -0.000005, 0.000005,
                    0.000001, -0.000001,

                }),
                #endregion
        }
    },
};
```

プログラムを簡単にするために、1月0日からの経過日数をDateTimeOffsetの差、TimeSpan.TotalDaysで得ているが、うるう年やうるう秒の処理について問題があるかもしれない。