├── Images └── .keep ├── Data.zip ├── Pictures ├── 银河系.png ├── B站视频.png ├── csv文件.png ├── 全部代码.png ├── 咸鱼卖家.jpg ├── 数据格式.png ├── 文件命名.png ├── 理论曲线.png ├── 理论曲线2.png ├── 观测地点.png ├── 观测时间.png ├── 频谱示例.png ├── 修正csv文件.png ├── 修正csv文件1.png ├── 推荐扫描点坐标.png ├── 文件时间属性.png ├── 银河系旋臂图1.png ├── 银河系旋臂图2.png ├── 银河系旋转曲线.png └── 把Data解压到同名文件夹.png ├── GetMilkyCoord.py ├── LICENSE ├── Rocurve.py ├── Main.py ├── Map.py ├── README.md └── MilkyWay.py /Images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Data.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Data.zip -------------------------------------------------------------------------------- /Pictures/银河系.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/银河系.png -------------------------------------------------------------------------------- /Pictures/B站视频.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/B站视频.png -------------------------------------------------------------------------------- /Pictures/csv文件.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/csv文件.png -------------------------------------------------------------------------------- /Pictures/全部代码.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/全部代码.png -------------------------------------------------------------------------------- /Pictures/咸鱼卖家.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/咸鱼卖家.jpg -------------------------------------------------------------------------------- /Pictures/数据格式.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/数据格式.png -------------------------------------------------------------------------------- /Pictures/文件命名.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/文件命名.png -------------------------------------------------------------------------------- /Pictures/理论曲线.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/理论曲线.png -------------------------------------------------------------------------------- /Pictures/理论曲线2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/理论曲线2.png -------------------------------------------------------------------------------- /Pictures/观测地点.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/观测地点.png -------------------------------------------------------------------------------- /Pictures/观测时间.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/观测时间.png -------------------------------------------------------------------------------- /Pictures/频谱示例.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/频谱示例.png -------------------------------------------------------------------------------- /Pictures/修正csv文件.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/修正csv文件.png -------------------------------------------------------------------------------- /Pictures/修正csv文件1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/修正csv文件1.png -------------------------------------------------------------------------------- /Pictures/推荐扫描点坐标.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/推荐扫描点坐标.png -------------------------------------------------------------------------------- /Pictures/文件时间属性.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/文件时间属性.png -------------------------------------------------------------------------------- /Pictures/银河系旋臂图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/银河系旋臂图1.png -------------------------------------------------------------------------------- /Pictures/银河系旋臂图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/银河系旋臂图2.png -------------------------------------------------------------------------------- /Pictures/银河系旋转曲线.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/银河系旋转曲线.png -------------------------------------------------------------------------------- /Pictures/把Data解压到同名文件夹.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BI6MHT/MilkyWay/HEAD/Pictures/把Data解压到同名文件夹.png -------------------------------------------------------------------------------- /GetMilkyCoord.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Jan 18 01:13:02 2022 4 | 5 | @author: BI6MHT 6 | """ 7 | from astropy.coordinates import SkyCoord 8 | import astropy.units as u 9 | 10 | # 无关文件 11 | # 获取银纬0°时,每隔5°对应赤经赤纬 12 | # 0°和360°是一样的 13 | 14 | for i in range(0,91,5): 15 | 16 | Coord = str(i)+'d'+' '+'0d' 17 | target = SkyCoord(Coord, frame="galactic",unit = u.deg) 18 | Ra = target.icrs.ra.to_string(u.hour) 19 | Dec = target.icrs.dec.to_string(u.deg) 20 | print(str(i)+' '+Ra+' '+Dec) 21 | 22 | for i in range(270,360,5): 23 | Coord = str(i)+'d'+' '+'0d' 24 | target = SkyCoord(Coord, frame="galactic", unit = u.deg) 25 | Ra = target.icrs.ra.to_string(u.hour) 26 | Dec = target.icrs.dec.to_string(u.deg) 27 | print(str(i)+' '+Ra+' '+Dec) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 BI6MHT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Rocurve.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Jan 14 23:32:00 2022 4 | 5 | @author: BI6MHT 6 | """ 7 | import numpy as np 8 | import pandas as pd 9 | import matplotlib.pyplot as plt 10 | 11 | # 太阳距离银心的距离和旋转速度 12 | R0 = 8.5 #kpc 13 | V0 = 220 #km/s 14 | 15 | # 读取文件 16 | table = pd.read_csv('MilkyWay.csv') 17 | #返回行数 18 | rows_num = table.shape[0] 19 | 20 | # Outfile 21 | 22 | R_list = [] 23 | V_list = [] 24 | 25 | for i in range(0,rows_num): 26 | # 读取第一行的内容 27 | myRow = table.iloc[i,:] 28 | # 去掉赤经赤纬 29 | myRow = myRow[2:len(myRow)] 30 | # 读取银经银维 31 | GLON = myRow[0] 32 | GLAT = myRow[1] 33 | 34 | # 银经0到90,270到360采用的算法 35 | if GLON<=90 or GLON>=270: 36 | # 获取最大的速度 37 | vmax = max(myRow[2:len(myRow)]) 38 | # 计算距离和旋转速度 39 | R = R0*np.sin(GLON*np.pi/180) 40 | V = vmax + V0*np.sin(GLON*np.pi/180) 41 | R_list.append(R) 42 | V_list.append(V) 43 | 44 | R_array = np.array(R_list) 45 | V_array = np.array(V_list) 46 | 47 | # 按距离排序,得到索引 48 | Order = R_array.argsort() 49 | 50 | # 利用索引对速度和距离进行排序 51 | V_array = V_array[Order] 52 | R_array = R_array[Order] 53 | 54 | # 用5次多项式拟合,输出系数从高到低 55 | Coe = np.polyfit(R_array, V_array, 5) 56 | # 生成多项式 57 | Pol = np.poly1d(Coe) 58 | # 得到结果 59 | V_Smooth = Pol(R_array) 60 | 61 | plt.rcParams['font.sans-serif']=['SimHei']; 62 | plt.rcParams['axes.unicode_minus'] = False; 63 | 64 | plt.xlim(0,10) 65 | plt.ylim(0,300) 66 | plt.title('银河旋转曲线') 67 | plt.xlabel('到银心的距离[kpc]') 68 | plt.ylabel('旋转速度[km/s]') 69 | plt.scatter(R_array,V_array,marker='x',color='b') 70 | plt.plot(R_array, V_Smooth,'r') 71 | plt.plot() 72 | plt.savefig('rocurve.png') -------------------------------------------------------------------------------- /Main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jan 17 16:22:39 2022 4 | 5 | @author: BI6MHT 6 | """ 7 | 8 | # 批量处理 9 | 10 | import os 11 | import datetime 12 | import pandas as pd 13 | from MilkyWay import MilkyWay 14 | 15 | 16 | obs_Dir = 'Data/' 17 | obs_Site = '110.81285 32.59175 40' 18 | 19 | # 读取目录下的文件 20 | obs_Sources = os.listdir(obs_Dir) 21 | 22 | 23 | """ 24 | 根据文件创建时间返回UTC时间 25 | 具体的是以修改时间还是创建时间为观测时间,还是要看看文件的时间属性的 26 | 如果要以修改时间为观测时间,可把LocalTime = os.stat(obs_Dir+obs_Source+'.txt').st_ctime中的st_ctime改为st_mtime 27 | """ 28 | def get_ObsTime(obs_Source): 29 | # 获取文件创建时间,类型为float 30 | LocalTime = os.stat(obs_Dir+obs_Source+'.txt').st_ctime 31 | # 获取文件的UTC时间,类为'datetime.datetime' 32 | UTCTime = datetime.datetime.utcfromtimestamp(LocalTime) 33 | # 将UTC时间转为字符串 34 | obs_Time = UTCTime.strftime("%Y-%m-%d %H:%M:%S") 35 | 36 | return obs_Time 37 | 38 | # 用于列表排序,即根据第2个元素进行排序 39 | def takeSecond(elem): 40 | return elem[1] 41 | 42 | List = [] 43 | 44 | for i in obs_Sources: 45 | 46 | # 去除txt后缀的文件名,得到观测目标的天球坐标 47 | obs_Source = i.split('.',1)[0] 48 | 49 | # 得到观测时间 50 | obs_Time = get_ObsTime(obs_Source) 51 | Hydro = MilkyWay(obs_Dir,obs_Site,obs_Source,obs_Time) 52 | 53 | # 只画出银经0°到90°,270°到360°的频谱 54 | # if 0<=Hydro.obs_GalLon<=90 or 270<=Hydro.obs_GalLon<=360: 55 | Hydro.get_Plot() 56 | child_List = [Hydro.obs_Source,Hydro.obs_GalLon,Hydro.obs_GalLat] 57 | rv_Array = Hydro.rv_Array.tolist() 58 | child_List = child_List+rv_Array 59 | List.append(child_List) 60 | 61 | # 根据递增的银河经度进行排序 62 | List = sorted(List,key=takeSecond) 63 | 64 | # 储存到excel表格中 65 | # 注意,如果再次运行储存csv操作,请确保上次的MilkyWay文件没有被打开 66 | List_Excel = pd.DataFrame(List) 67 | List_Excel.to_csv('MilkyWay.csv') 68 | -------------------------------------------------------------------------------- /Map.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Jan 15 13:58:29 2022 4 | 5 | @author: BI6MHT 6 | """ 7 | import numpy as np 8 | import pandas as pd 9 | import matplotlib.pyplot as plt 10 | import cmath 11 | 12 | # 太阳距离银心的距离和旋转速度 13 | R0 = 8.5 #kpc 14 | V0 = 220 #km/s 15 | 16 | # 读取文件 17 | table = pd.read_csv('MilkyWay.csv') 18 | 19 | #返回行数 20 | rows_num = table.shape[0] 21 | #print(rows_num) 22 | # Outfile 23 | 24 | R_list = [] 25 | V_list = [] 26 | fig = plt.figure() 27 | ax = fig.add_subplot(111) 28 | 29 | for i in range(0,rows_num): 30 | # 读取第i行的内容 31 | myRow = table.iloc[i,:] 32 | #print(myRow) 33 | # 去掉赤经赤纬 34 | myRow = myRow[2:len(myRow)] 35 | #print(myRow) 36 | # 读取银经银维 37 | GLON = myRow[0] 38 | GLAT = myRow[1] 39 | 40 | # 除去最后一行的最大径向速度 41 | vels = myRow[2:len(myRow)-1] 42 | #print(vels) 43 | theta = GLON -90 + 180; 44 | for VR in vels: 45 | 46 | # 如果为NaN或者None,则跳过本次循环 47 | if pd.isnull(VR): 48 | continue 49 | 50 | # 求出峰值速度对应的到银心的距离 51 | R = R0*V0*np.sin(GLON*np.pi/180)/(V0*np.sin(GLON*np.pi/180)+VR) 52 | 53 | # 计算分子云的x,y坐标轴 54 | rp = cmath.sqrt(R**2-R0**2*(np.sin(GLON*np.pi/180))**2)+R0*np.cos(GLON*np.pi/180) 55 | rm = -cmath.sqrt(R**2-R0**2*(np.sin(GLON*np.pi/180))**2)+R0*np.cos(GLON*np.pi/180) 56 | 57 | 58 | plt.rcParams['font.sans-serif']=['SimHei']; 59 | plt.rcParams['axes.unicode_minus'] = False; 60 | 61 | # 如果有两者都为实数,显然有rm0: 64 | 65 | # rp.real>0的时候,rm.real<0,则说明两个都为实数 66 | # 因为负数开平方根只能开出来虚数,对实部的正负无影响 67 | # 故如果是复数的话,两者的实部部分正负是相同的。 68 | if rm.real<0: 69 | r = rp.real 70 | x = r*np.cos(theta*np.pi/180) 71 | y = -R0 + r*np.sin(theta*np.pi/180) 72 | # 作图 73 | plt.scatter(x,y,marker='x',c='black') 74 | 75 | # rp.real>0,且rm.real>=0,两者又都为实数 76 | # 故有两个解存在 77 | elif rp.imag == 0 and rm.imag == 0: 78 | # 发现二解 79 | # 处理这些的最简单的方法是把它们都画出来,但是用另一种颜色 ' 80 | r = rp.real; 81 | x = r*np.cos(theta*np.pi/180) 82 | y = -R0+ r*np.sin(theta*np.pi/180) 83 | # 画图 84 | plt.scatter(x,y,marker='x',c='r') 85 | r = rm.real 86 | x = r*np.cos(theta*np.pi/180) 87 | y = -R0 + r*np.sin(theta*np.pi/180) 88 | # 画图 89 | plt.scatter(x,y,marker='x',c='b') 90 | #print([GLON,VR,rp,rm]) 91 | 92 | # 画出银河和太阳的位置 93 | plt.text(0, 0, 'C') 94 | 95 | plt.text(0, -8.5, 'Sun') 96 | 97 | plt.text(-15, 15, 'Q1') 98 | 99 | plt.text(-15, -15, 'Q2') 100 | 101 | plt.text(15, -15, 'Q3') 102 | 103 | plt.text(15, 15, 'Q4') 104 | 105 | # 设置画图极限,即0到10kpc,0到300km/s 106 | 107 | 108 | plt.xlim(-25,25) 109 | plt.ylim(-25,25) 110 | # 设置为正方形 111 | ax.set_aspect('equal', adjustable='box') 112 | plt.title('银河旋臂图') 113 | plt.xlabel('[kpc]') 114 | plt.ylabel('[kpc]') 115 | plt.savefig('map.png') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 零.原理部分 2 | 3 | 关于处理氢谱线绘制银河旋转曲线的原理部分可见于[【射电望远镜】绘制银河系旋转曲线](https://www.bilibili.com/video/BV1VX4y1g7jU?spm_id_from=333.999.0.0) 4 | 5 | 当然,也可以直接略过,用代码来处理数据 6 | 7 | 声明:代码的算法不太精确,估计只能看个大概 8 | 9 | ## 一.文件命名 10 | 11 | 为了方便数据的处理,文件命名如[图1](#文件命名)所示,即'赤经 赤纬',注意空格 12 | 13 | 例如17h4m为赤经,当然精确到s也是可以的,比如17h4m3s 14 | 15 | 41d为赤纬,也可精确到小数点后,比如41.23214d 16 | 17 | 18 | ![文件命名](Pictures/文件命名.png) 19 |
图1:文件命名
20 | 21 | 22 | ## 二.数据格式 23 | 24 | 通过SDRSharp的IF average进行积分平均后的频谱txt文件格式如[图2](#数据格式)所示 25 | 26 | 第一行与积分平均的次数有关,不用考虑 27 | 28 | 第一列是观测频率,单位是MHz 29 | 30 | 第二列是信号强度,单位应该是电压,即V 31 | 32 | ![数据格式](Pictures/数据格式.png) 33 |
图2:数据格式
34 | 35 | 36 | ## 三.观测时间和地点 37 | 38 | Main.py文件中代码是根据文件的创建时间来返回UTC时间的,如[图3.1](#观测时间)所示 39 | 40 | ![观测时间](Pictures/观测时间.png) 41 |
图3.1:观测时间
42 | 43 | 文本文件(频谱文件)有创建时间,修改时间,访问时间三种时间属性,如图[图3.2](#文件时间属性);文件重命名不会改变任意一种时间属性;硬盘上进行拷贝的话应该会改变创建和访问时间,但是修改时间不会变动。 44 | 45 | ![文件时间属性](Pictures/文件时间属性.png) 46 |
图3.2:文件时间属性
47 | 48 | 然而把数据直接上传到gitee仓库中后,再下载回来的话,文本文件所有时间属性就都变为下载时刻的时间了;所以便把数据压缩到了Data.zip这个压缩包中,大家下载后可以解压为目录下同名文件夹,如[图3.3](#把Data解压到同名文件夹)。然而仍然存在一个问题,如果用windows自带的解压,解压后文件的创建时间才是观测时间 ,如果用我电脑上Bandzip这个解压工具进行解压,解压后文件的修改时间才是观测时间。于是在此 **便默认大家都用Win10自带的解压,创建时间才是观测时间** 。 49 | 50 | 当然,具体是哪个时间属性才是观测时间,可以观察一下文件的属性,如果有一个时间属性不同文件时不同的,那应该便是观测时间了。如果你想 **以文件的修改时间为观测时间** ,可以将[图3.1](#观测时间)LocalTime = os.stat(obs_Dir+obs_Source+'.txt').st_mtime中的 **st_mtime改为st_ctime** 。 51 | 52 | ![把Data解压到同名文件夹](Pictures/把Data解压到同名文件夹.png) 53 |
图3.3:把Data解压到同名文件夹
54 | 55 | 观测地点可在Main.py文件中进行修改,如[图3.4](#观测地点)所示。obs_Site = '110.81285 32.59175 40'中的110.81285代表经度,32.59175代表纬度,40代表海拔为40m。 56 | 57 | ![观测地点](Pictures/观测地点.png) 58 |
图3.4:观测地点
59 | 60 | ## 四.示例数据来源 61 | 62 | 在湖北省十堰市观测的氢谱线数据,利用赤道仪进行天体定位,天线为栅格抛物面,该天线在曾在B站“从零开始的业余射电天文”系列中介绍过,见于[图4.1](#B站视频), 63 | 64 | [点击此转到视频链接](https://www.bilibili.com/video/BV1Sh411b7fV?spm_id_from=333.999.0.0); 65 | 66 | ![B站视频](Pictures/B站视频.png) 67 |
图4.1:B站视频
68 | 69 | 其中放大滤波器没有采用视频中推荐的,而采用了咸鱼某款,如[图4.2](#咸鱼卖家)所示的蓝色板子,1420MHz具有40-50dB的增益,噪声系数也很低。 70 | 71 | ![咸鱼卖家](Pictures/咸鱼卖家.jpg) 72 |
图4.2:咸鱼卖家
73 | 74 | 75 | 76 | ## 五.代码适用范围 77 | 78 | 旋转曲线代码适合处理在银纬0°附近,银经0°到90°和270°到360°的观测到的氢谱线 79 | 80 | 旋臂成像代码可以处理在银纬0°附近,所有银经范围内的氢谱线。 81 | 82 | 在银纬0°附近,不同银经对应的赤经赤纬如[图5](#推荐扫描点坐标) 83 | 84 | 第一列为银经,第二列为赤经,第三列为赤纬。 85 | 86 | ![推荐扫描点坐标](Pictures/推荐扫描点坐标.png) 87 |
图5:推荐扫描点坐标
88 | 89 | 90 | 91 | ## 六.python的使用 92 | 93 | 在运行代码前,请确保你的python库中含有“astropy,numpy 94 | ,matplotlib,pandas,scipy”这五个包 95 | 96 | 或者直接安装anaconda这个软件,里面集成了很多科学计算的包,但没有astropy这个包,不过在anaconda可以手动安装这个包 97 | 98 | 如果在anaconda自带的编译器spyder中直接运行Main.py文件,当它发现缺少astropy这个包时,会自动下载。不过默认的包下载地址应该在国外,所以下载速度有些慢 99 | 100 | python安装包或者安装anaconda的教程可见于B站,或者直接使用搜索引擎 101 | 102 | 全部的代码文件如[图6.1](#全部代码)所示 103 | 104 | ![全部代码](Pictures/全部代码.png) 105 |
图6.1:全部代码
106 | 107 | 108 | 所有的频谱txt文件均放于Data,运行Main.py文件 109 | 110 | 可以得到 **氢谱线相对于日心的径向速度** vs **谱线的等效温度(即强度)** 的频谱图,如[图6.2](#频谱示例)所示;上方的图是未经平滑的频谱,下方是平滑后的频谱;最上方的文字代表了**观测目标的赤经赤纬**以及**UTC观测时间**。所有生成的频谱图均存放在Images文件中,以观测目标所在银经进行命名。 111 | 112 | ![频谱示例](Pictures/频谱示例.png) 113 |
图6.2:频谱示例
114 | 115 | 116 | 同时也可以得到一个名为MilkyWay.csv的excel文件,里面储存着关于频谱的赤经赤纬,银经银纬,峰值处的径向速度,最大径向速度,不过暂时不用这些。 117 | 118 | ## 七.银河系旋转曲线的绘制,有暗物质存在? 119 | 120 | 在确保已经成功运行过Main.py文件的前提下,直接运行rocurve.py 121 | ,便可以得到银河系旋转曲线,如[图7.1](#银河系旋转曲线)所示。 122 | 其中横坐标是氢分子团距离银心的距离,纵坐标是绕银心转动的速度。 123 | 124 | ![银河系旋转曲线](Pictures/银河系旋转曲线.png) 125 |
图7.1:银河系旋转曲线
126 | 127 | 理论上的旋转曲线应当如[图7.2](#理论曲线)中的红线或者[7.3](#理论曲线2)粉红虚线所示,即在超过一定距离后,天体运行遵循开普勒第三定律,转动速度不断下降。 128 | 129 | ![理论曲线](Pictures/理论曲线.png) 130 |
图7.2:理论曲线
131 | 132 | ![理论曲线](Pictures/理论曲线2.png) 133 |
图7.3:理论曲线
134 | 135 | 而从实际测量的曲线来看,转动速度在核心区陡峭上升,因为这里时星系质量集中的区域,转动规律类似于刚体转动速度在边缘区并没有随距离的增加而下降,反而保持着平坦不变的趋势 136 | 137 | 对这种现象的解释如下: 138 | 139 | 银河系有很大一部分质量不在星系的中心区域,而在边缘区以外很远的银晕中。那里几乎没有什么恒星,可观测到的发光物也很少。大多数的质量属于不发光的暗物质,即那些不发出任何辐射因而探测不到的物质。 140 | 141 | 142 | ## 八.银河系旋臂的绘制,银河系存在旋臂结构? 143 | 144 | 在绘制银河系旋臂前,需要对MilkyWay.csv中的数据略加修改。如果不加以修改,在确保已经成功运行过Main.py文件的前提下,直接运行Map.py,可得到[图8.1](#银河系旋转曲线),其中Q1,Q2,Q3,Q4分别代表银河系的第1,2,3,4象限,就是把银河系平面以太阳为中心,分成四个象限;C代表银河系的中心,Sun代表太阳;单位kpc中的pc即秒差距,是距离单位,k即1000之意 145 | 146 | ![银河系旋臂图1](Pictures/银河系旋臂图1.png) 147 |
图8.1:直接运行Map.py得到的银河系旋臂图
148 | 149 | 尽管有些乱,但仍可以看出几条旋臂,对比[图8.2](#银河系),可以看出四条臂分别是英仙臂、外臂以及太阳所在的猎户臂 150 | 151 | ![银河系](Pictures/银河系.png) 152 |
图8.2:银河系旋臂
153 | 154 | ## 九.修正MilkyWay.csv文件 155 | 156 | 我们所需的只是频谱图上峰值对应径向速度,以及最大径向速度 157 | 158 | 下面我们看一下MilkyWay.csv里面的数据格式,如[图9.1](#csv文件) 159 | 160 | ![csv文件](Pictures/csv文件.png) 161 |
图9.1:csv文件
162 | 163 | 164 | 第一列是观测目标(即一个观测文件)的编号,第二列是观测目标的赤经赤纬,第三列是观测目标的银经,第四列是观测目标的银维。 165 | 166 | 剩余的列便是峰值的对应径向速度和最大径向速度了。 167 | 168 | 其中每一行的最后一列就是最大径向速度,是用来求上面的银河系旋转曲线的,不用修改。 169 | 170 | 求峰值由于代码是用的求极值的算法,所以会求出一系列的极值峰,但是只有一两个才是真正的氢谱线峰,我们必须观察Images文件夹中生成的频谱图,去掉那些不是氢谱线的峰对应的径向速度。 171 | 172 | [图9.1](#csv文件)中灰色的那一行对应[图6.2](#频谱示例)中的频谱图,频谱图上标记红点的都注明了坐标,坐标第一个值是径向速度,第二个值是等效温度,我们只关注径向速度。 173 | 174 | 其中最右边的红点标注的是最大径向速度,即MilkyWay.csv中每一行的最后一列所对应的值,不必修改。观察频谱图后我们发现-133.712处不是峰值,故直接删去,得到如[图9.2](#修正csv文件)所示。 175 | 176 | ![修正csv文件](Pictures/修正csv文件.png) 177 |
图9.2:修正csv文件
178 | 179 | 对于多数频谱应该只能看出一个峰,少数有两个峰。我们对观测的每个源对应的每一行都做如上处理,可得到修正后的MilkyWay.csv文件,大概如图[图9.3](#修正csv文件1)所示 180 | 181 | ![修正csv文件1](Pictures/修正csv文件1.png) 182 |
图9.3:修正后的csv文件
183 | 184 | 185 | 这时候候便可运行Map.py文件进行成像,得到[图9.4](#银河系旋臂图2) 186 | 187 | ![银河系旋臂图2](Pictures/银河系旋臂图2.png) 188 |
图9.4:银河系旋臂图
189 | 190 | 黑色代表算法的唯一解,描绘出了英仙臂和外臂;蓝色和红色代表算法的两个解,个人认为蓝色是对的,即太阳所在的猎户臂。 191 | 192 | 注意:一个观测方向上有几个峰就能画出几个旋臂上的点,我们观测到的基本上都是双峰或者单峰,故在一个观测方向上只能画出一或两条旋臂上的点。 193 | -------------------------------------------------------------------------------- /MilkyWay.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Jan 17 09:19:39 2022 4 | 5 | @author: BI6MHT 6 | """ 7 | 8 | from astropy.coordinates import SkyCoord,Angle,EarthLocation 9 | from astropy.time import Time 10 | from astropy.constants import c,k_B 11 | import astropy.units as u 12 | import numpy as np 13 | import matplotlib.pyplot as plt 14 | 15 | # 对曲线进行平滑 16 | #import statsmodels.api as sm 17 | #lowess = sm.nonparametric.lowess 18 | 19 | # 寻求极大值;对曲线进行平滑 20 | from scipy import signal 21 | 22 | # 单个天体的获取 23 | 24 | class MilkyWay(): 25 | 26 | 27 | """ 28 | 初始化类的属性 29 | 30 | obs_Dir:观测文件所在根目录 31 | 32 | obs_Site:观测者在地球上所在位置,例如 33 | 34 | obs_Source:观测源的赤经赤纬,例如'12h10m0s 20d' 35 | 36 | obs_Time:观测的UTC时间,例如'2021-10-30 13:00:43' 37 | 38 | smooth_Window(默认值31): 即Savitzky-Golay滤波器的窗口长度,值越小,曲线越贴近真实曲线;值越大,平滑效果越厉害(备注:该值必须为正奇整数) 39 | 40 | smooth_k(默认值3): 值越大,曲线越贴近真实曲线;值越小,曲线平滑越厉害。另外,当k值较大时,受窗口长度限制,拟合会出现问题,高频曲线会变成直线。 41 | 42 | """ 43 | def __init__(self,obs_Dir,obs_Site,obs_Source,obs_Time,smooth_Window=21,smooth_k=3): 44 | 45 | self.obs_Freq = 1420.4057517667 46 | 47 | self.obs_Dir = obs_Dir 48 | self.obs_Site = obs_Site 49 | self.obs_Source = obs_Source 50 | self.obs_Time = obs_Time 51 | self.smooth_Window = smooth_Window 52 | self.smooth_k = smooth_k 53 | self.obs_GalLon,self.obs_GalLat = self.get_GalCoord() 54 | 55 | # 获取频谱参数,第一列为频率(MHz);第二列是强度,默认单位为W 56 | self.spec = np.loadtxt(self.obs_Dir+self.obs_Source+'.txt',dtype=float,skiprows=1) 57 | 58 | # 取-150到150km/s之间的径向速度rv,以及对应的等效温度(平滑和未平滑) 59 | self.rv, self.equiTemp_Smooth,self.equiTemp = self.get_Smooth() 60 | 61 | # 得到峰值点以最大径向速度那一点 62 | # 按峰值降序排列 63 | # 最大速度那一点对应末端元素 64 | self.rv_Array,self.T_Array = self.get_Peaks() 65 | 66 | """ 67 | 获取源所在的银经和银维 68 | 返回类型:list类型 69 | 返回结果:银经 银维 70 | 返回例子:'54.2663 0.224174' 71 | 72 | """ 73 | def get_GalCoord(self): 74 | 75 | Source = SkyCoord(self.obs_Source,frame='icrs') 76 | Gal = Source.galactic.to_string() 77 | Gal_lon = float(Gal.split(' ')[0]) 78 | Gal_lat = float(Gal.split(' ')[1]) 79 | return Gal_lon,Gal_lat 80 | 81 | """ 82 | 根据多普勒效应,获取不同频率对应的修正到日心的径向速度 83 | 返回类型:一维的numpy.ndarray 84 | 返回结果:不同频率对应的径向速度 85 | 返回例子:[1098.31559636 ... -1005.95448058 -1008.01546988] 86 | """ 87 | def get_CorrectVel(self): 88 | 89 | obs_Time = Time(self.obs_Time) 90 | 91 | obs_SiteLon = Angle(self.obs_Site.split(' ')[0],unit=u.deg) 92 | obs_SiteLat = Angle(self.obs_Site.split(' ')[1],unit=u.deg) 93 | obs_SiteHei = float(self.obs_Site.split(' ')[2])*u.m 94 | loc = EarthLocation.from_geodetic(obs_SiteLon,obs_SiteLat,obs_SiteHei) 95 | 96 | # 观测天体源所在的赤经赤纬,坐标框架采用icrs,即天球坐标系坐标 97 | Source = SkyCoord(self.obs_Source,frame='icrs') 98 | 99 | # 读取频谱文件,获取观测频点 100 | 101 | spec_freq = self.spec[:,0] 102 | 103 | # 地球上观测者和中性氢在视线上的相对速度,即径向速度 104 | # 根据多普勒效应,不同频点对应的不同径向速度 105 | rv = (self.obs_Freq-spec_freq)*c/self.obs_Freq 106 | 107 | # 径向速度vr是观测者和中性氢在视线上的相对速度 108 | # 现在我们要求得以太阳系质心为原点时(或者大概认为是在太阳中心观测),太阳中心和中性氢在两者连线上的相对速度 109 | # 即进行坐标转换,因而要求出一个速度修正项 110 | vcorr = Source.radial_velocity_correction(kind='barycentric',obstime=obs_Time, location=loc) 111 | 112 | # 加上速度修正项以后,便得到了在太阳上观测中性氢的径向速度 113 | # m/s转成km/s,然后取其数值 114 | rv = (rv + vcorr + rv*vcorr/c) 115 | rv = rv.to(u.km/u.s).to_value() 116 | 117 | # 显然,频率较大(蓝移)时,rv为负;频率较小(红移)时,rv为正 118 | # 所以目前rv是的排序是由正到负的,不妨让rv逆序排列 119 | rv = rv[::-1] 120 | 121 | return rv 122 | 123 | """ 124 | 获取频率强度(电平)对应的等效温度 125 | 返回类型:一维的numpy.ndarray 126 | 返回结果:不同径向速度(频率)对应的等效温度 127 | 返回例子:[1.31519478 1.26451874 ... 1.40664323 1.37319388 1.22032352] 128 | """ 129 | def get_EquiTemp(self): 130 | 131 | spec_freq = self.spec[:,0] 132 | spec_level = self.spec[:,1] 133 | 134 | # 观测带宽,由于MHz,故乘以10^6 135 | spec_width = (spec_freq[-1]-spec_freq[0])*np.power(10,6) 136 | 137 | # 根据奈奎斯特噪声或者说热噪声,有P=kBT,其中P为功率,k为玻尔兹曼常数,B为带宽,T为温度 138 | # 即我们可以将功率等效为温度 139 | # 由于等效出来的数量级过大,故可乘以10^(-27)以减小数量级 140 | equiTemp = (spec_level/k_B/spec_width)*pow(10,-14) 141 | 142 | # 由于径向速度rv是逆序排列的,故另EquiTemp也逆向排序,以和rv一一对应 143 | equiTemp = equiTemp[::-1] 144 | 145 | # 由于等式中出现从astropy中调用的k_B常量,故equiTemp是 146 | # 取其数值 147 | equiTemp = equiTemp.to_value() 148 | 149 | return equiTemp 150 | 151 | """ 152 | 获取-150到150km/s之间的径向速度rv以及对应的等效温度T,并进行平滑处理 153 | 返回类型:一维的numpy.ndarray,一维的numpy.ndarray 154 | 返回结果:径向速度,等效温度 155 | 返回例子:[-148.58293111 ... 146.13853902],[1.31519478 ... 1.22032352] 156 | """ 157 | def get_VT(self): 158 | 159 | rv = self.get_CorrectVel() 160 | equiTemp = self.get_EquiTemp() 161 | 162 | rv_index=np.where((rv>-150)&(rv<150))[0] 163 | rv_start = rv_index[0] 164 | rv_end = rv_index[-1] 165 | rv_150 = rv[rv_start:rv_end] 166 | equiTemp_150 = equiTemp[rv_start:rv_end] 167 | 168 | return rv_150,equiTemp_150 169 | 170 | 171 | # def get_Smooth(self): 172 | # rv, equiTemp = self.get_VT() 173 | # # 对数值进行平滑化 174 | # # 其中的1/20代表平滑程度,可自行调整 175 | # T_Smooth = lowess(equiTemp,rv,frac=self.Smooth)[:,1] 176 | # return rv,T_Smooth,equiTemp 177 | 178 | 179 | """ 180 | 对 径向速度rv vs 等效温度equiTemp 的曲线进行平滑处理 181 | 其中vr在-150km/s到150km/s之间 182 | 返回类型:一维的numpy.ndarray,一维的numpy.ndarray,一维的numpy.ndarray 183 | 返回结果:径向速度,平滑等效温度,未平滑等效温度 184 | 返回例子:[-148.58293111 ... 146.13853902],[1.31519478 ... 1.22032352],[1.31519478 ... 1.22032352] 185 | """ 186 | def get_Smooth(self): 187 | rv, equiTemp = self.get_VT() 188 | # 对数值进行平滑 189 | 190 | T_Smooth = signal.savgol_filter(equiTemp,self.smooth_Window,self.smooth_k) 191 | return rv,T_Smooth,equiTemp 192 | 193 | """ 194 | 获取频谱图 195 | """ 196 | def get_Plot(self): 197 | plt.figure() 198 | 199 | #设置使用的字体为支持中文的字体 200 | plt.rcParams['font.sans-serif']=['SimHei']; 201 | plt.rcParams['axes.unicode_minus'] = False; 202 | 203 | # 未平滑 204 | plt.subplot(2,1,1) 205 | plt.plot(self.rv,self.equiTemp) 206 | plt.title(self.obs_Source + ' ' + self.obs_Time) 207 | 208 | # 平滑 209 | plt.subplot(2,1,2) 210 | plt.plot(self.rv,self.equiTemp_Smooth); 211 | plt.xlabel('中性氢分子团相对于日心的径向速度vr(km/s)') 212 | plt.ylabel('等效温度T(K)') 213 | 214 | # 画出峰值,以及最大径向速度那一点 215 | for i in range(0,len(self.T_Array)): 216 | plt.scatter(self.rv_Array[i],self.T_Array[i],s=25,c='r') 217 | plt.text(self.rv_Array[i],self.T_Array[i],\ 218 | str(np.around(self.rv_Array[i],3))+','+str(np.around(self.T_Array[i],3)),\ 219 | fontdict={'fontsize':8}); 220 | 221 | # 以银经命名,银经保留一位小数 222 | plt.savefig('Images/'+str(np.around(self.obs_GalLon,1))+'.png') 223 | 224 | 225 | plt.clf() # 清图 226 | plt.cla() # 清坐标轴 227 | plt.close() # 关窗口 228 | 229 | """ 230 | 寻求最大峰以及右边的峰,即求极值问题 231 | 返回类型:一维的numpy.ndarray,一维的numpy.ndarray 232 | 返回结果:峰的径向速度以及最大径向速度,对应的等效温度 233 | 返回例子:[-148.58293111 ... 146.13853902],[1.31519478 ... 1.22032352] 234 | """ 235 | def get_Peaks(self): 236 | 237 | # 观察频谱规律后,对于银经0到90°,270°到360°,寻峰算法如下 238 | 239 | # 求取最高峰所在位置 240 | max_Index = np.argmax(self.equiTemp_Smooth) 241 | # 峰值间的最小间隔点数,两峰之间的间隔需大于此 242 | # 此处设两峰之间的间隔需大于30km/s 243 | lim_Distance = np.ceil((30*len(self.equiTemp_Smooth)/300)) 244 | # 寻峰,即求极值点 245 | # 此处只求最大峰及其左边的峰 246 | peaks =signal.find_peaks(self.equiTemp_Smooth[0:max_Index+2],distance = lim_Distance)[0] 247 | 248 | # 找出极大值对应的径向速度和等效温度 249 | T_Array = np.array([]) 250 | rv_Array = np.array([]) 251 | for peak in peaks: 252 | T_Array= np.append(T_Array,self.equiTemp_Smooth[peak]) 253 | rv_Array = np.append(rv_Array,self.rv[peak]) 254 | 255 | # 按峰值降序排序,得到索引 256 | Order = T_Array.argsort()[::-1] 257 | # 利用索引对径向速度和等效温度进行排序 258 | rv_Array = rv_Array[Order] 259 | T_Array = T_Array[Order] 260 | 261 | # 在尾端加入最大径向速度 262 | mark_Vel,mark_T = self.get_MaxVel() 263 | 264 | rv_Array = np.append(rv_Array,mark_Vel) 265 | T_Array = np.append(T_Array,mark_T) 266 | 267 | 268 | return rv_Array,T_Array 269 | 270 | """ 271 | 获取氢谱线峰的最大径向速度,以及对应的等效温度 272 | 峰是弥散开来,可以求右边最高峰衰减到一定值处对应的速度 273 | 返回类型: float 274 | 返回结果:最大径向速度,等效温度 275 | """ 276 | def get_MaxVel(self): 277 | 278 | # 此处以(右边最高峰)衰减到(右边最高峰)的右边频谱积分平值为截止标准 279 | max_Index = np.argmax(self.equiTemp_Smooth) 280 | mark_Index = self.get_RightFirst(self.equiTemp_Smooth,max_Index) 281 | mark_Vel = self.rv[mark_Index] 282 | mark_T = self.equiTemp_Smooth[mark_Index] 283 | 284 | return mark_Vel,mark_T 285 | 286 | """ 287 | 返回第一个小于右边频谱积分平值的索引 288 | 返回类型: int 289 | 返回结果:索引 290 | """ 291 | 292 | def get_RightFirst(self,Arr,Index): 293 | 294 | # 边缘峰的右边频谱的积分平均值 295 | # 不能用[Index:-1],可以实际测试一下 296 | spec_Ave = np.sum(Arr[Index:len(Arr)])/len(Arr[Index:len(Arr)]) 297 | # 寻求第一个小于spec_Ave对应的索引 298 | for i in range(Index,len(Arr)): 299 | i+1 300 | if Arr[i] <= spec_Ave: 301 | break 302 | return i 303 | --------------------------------------------------------------------------------