赤道仪自动寻星V2.0(代码部分)

        以下是赤道仪自动寻星V2.0的代码部分,自动寻星使用C++类编写,利用OpenCV编写了图形用户界面,在主函数中只调用一句话即可。请读者先阅读笔者发布的纯算法部分,以下代码均严格按照论文所述编写。需要说明的是,以下代码都使用了命名空间lsx,是笔者的姓名缩写。

        本文首先介绍类AutoStarFindController的组成,这个类是自动寻星最终实现的类。为了让读者明白这个类中其他类的功能和作用,本文将从底层的类进行介绍,最后介绍类AutoStarFindController及其运行的样子。

类 AutoStarFindController
成员
targetTableastronomy::ObservationTable星表,是观测目标的集合
start, endastronomy::ObservationTarget自动寻星的出发位置和目标位置的2个天体
zero2starbool判断是否是从零位开始的标志位
ra_arrival, dec_arrivalboolRA和DEC转轴到达目标的标志位
pointTargetastronomy::ObservationTarget用户选择的天体
this_target_is_starbool当前的天体为恒星的标志位,每次自动寻星都通过对准恒星进行校准
接口函数
AutomaticStarFinding()void主函数中调用的自动寻星函数
preSet()void提前设置自动寻星参数

        从底层开始介绍各个类。首先介绍类:ObservationTarget,这是观测目标类,里面存放了一个观测目标的坐标名称等信息。

类ObservationTarget

Class ObservationTarget头文件:

		/**
		 * @brief 单个观测目标
		 */
		class ObservationTarget {

			public:
				double RA_angle = 0;//赤经角度坐标,单位:°
				double DEC_angle = 0;//赤纬角度坐标,单位:°

				double elevation = 0;//仰角,单位:°
				double azimuth = 0;//方位角,单位:°

				double RA_hour = 0;//赤经时
				double RA_min = 0;//赤经分
				double RA_second = 0;//赤经秒

				double DEC_degree = 0;//赤纬度
				double DEC_min = 0;//赤纬分
				double DEC_second = 0;//赤纬秒

				double position_in_image_x = 0;//该天体在自动寻星图像上的横坐标
				double position_in_image_y = 0;//该天体在自动寻星图像上的纵坐标

				bool error = true;//是否是错误星体
				bool isStar = false;//是否是恒星标志位
				bool origin_show = true;//一开始就展示名字

				std::string name = "赤道仪零位";
				std::string number = "赤道仪零位";
				std::string ShowName = "000";

				/** 
				 * @brief 设置观测天体坐标
				 * @param number 天体编号
				 * @param name 天体名称
				 * @param RA_hour 赤经小时
				 * @param RA_min 赤经分
				 * @param RA_second 赤经秒
				 * @param DEC_degree 赤纬度
				 * @param DEC_min 赤纬分
				 * @param DEC_second 赤纬秒
				 * @return void
				 */
				void inputTarget(std::string number, std::string name, std::string showName, \
					double RA_hour, double RA_min, double RA_second, double DEC_degree, double DEC_min, double DEC_second, \
					bool is_star, bool origin_show = true);

				/**
				 * @brief 打印天体参数
				 * @param none
				 * @return void
				 */
				void printTarget();

				/**
				 * @brief 判断是否为当前天体
				 * @param none
				 * @return void
				 */
				bool isName(std::string& inputName_or_Number);

				ObservationTarget(std::string number, std::string name, std::string showName, \
					double RA_hour, double RA_min, double RA_second, double DEC_degree, double DEC_min, double DEC_second,\
					bool is_star, bool origin_show = true);

				ObservationTarget();
			private:
		};

对应cpp代码

void lsx::astronomy::ObservationTarget::inputTarget(std::string number, std::string name, std::string showName, \
	double RA_hour, double RA_min, double RA_second, double DEC_degree, double DEC_min, double DEC_second, \
	bool is_star, bool origin_show) {
	this->RA_hour = RA_hour;
	this->RA_min = RA_min;
	this->RA_second = RA_second;
	this->DEC_degree = DEC_degree;
	this->DEC_min = DEC_min;
	this->DEC_second = DEC_second;
	this->RA_angle = lsx::astronomy::ra2degree(this->RA_hour, this->RA_min, this->RA_second);
	if (this->DEC_degree < 0 || this->DEC_min < 0 || this->DEC_second < 0) {
		this->DEC_degree = -fabs(this->DEC_degree);
		this->DEC_min = -fabs(this->DEC_min);
		this->DEC_second = -fabs(this->DEC_second);
	}
	this->DEC_angle = lsx::astronomy::dec2degree(this->DEC_degree, this->DEC_min, this->DEC_second);
	this->name = name;
	this->number = number;
	this->ShowName = showName;
	this->isStar = is_star;
	this->origin_show = origin_show;
}

void lsx::astronomy::ObservationTarget::printTarget() {
	std::cout <<"  "<< this->name << std::endl;
	std::cout << "  " << "RA:" << this->RA_hour << " h " << this->RA_min << " min " << this->RA_second << " sec " << "(" << this->RA_angle << "°)" << std::endl;
	std::cout << "  " << "DEC:" << this->RA_hour << " ° " << this->RA_min << " ′ " << this->RA_second << " ″ " << "(" << this->DEC_angle << "°)" << std::endl;
}

bool lsx::astronomy::ObservationTarget::isName(std::string& inputName_or_Number) {
	if (inputName_or_Number == this->name || inputName_or_Number == this->number) {
		return true;
	}
	else { 
		return false; 
	}
}

lsx::astronomy::ObservationTarget::ObservationTarget(std::string number, std::string name, std::string showName, \
	double RA_hour, double RA_min, double RA_second, double DEC_degree, double DEC_min, double DEC_second, \
	bool is_star, bool origin_show) {
	this->inputTarget(number, name, showName, RA_hour, RA_min, RA_second, DEC_degree, DEC_min, DEC_second, is_star, origin_show);
	this->error = false;
}

lsx::astronomy::ObservationTarget::ObservationTarget() {
	this->error = true;
}

        需要说明的是,命名空间astronomy不仅包含了这些类还有很多工具函数,他们都放置在了Astronomy.h和.cpp中,这个类和下面其他一些类是从命名空间astronomy中裁剪出来的。

类ObservationTable

        其次将要介绍类ObservationTable,该类利用C++容器将观测目标类ObservationTarget合成了一个列表,可以理解为自定义的星表。

Class ObservationTable 头文件:

		/**
		 * @brief 观测目标列表
		 */
		class ObservationTable {
		public:

			std::vector<ObservationTarget> TargetTable;

			/**
			 * @brief 在星表中插入一个新的天体,如果需要观测星表外的天体,可以通过该函数临时插入
			 * @param number 天体编号
			 * @param name 天体名称
			 * @param RA_hour 赤经小时
			 * @param RA_min 赤经分
			 * @param RA_second 赤经秒
			 * @param DEC_degree 赤纬度
			 * @param DEC_min 赤纬分
			 * @param DEC_second 赤纬秒
			 * @param is_star 是否是恒星的标志位,true:该天体为恒星;false:该天体不是恒星
			 * @return void
			 */
			void input_new_target(std::string number, std::string name, std::string showName, \
				double RA_hour, double RA_min, double RA_second, double DEC_degree, double DEC_min, double DEC_second, \
				bool is_star);

			/**
			 * @brief 在目标列表中得到观测目标
			 * @param name_or_number 名称或编号
			 * @return 通过名称或编号寻找的观测目标
			 */
			lsx::astronomy::ObservationTarget findTarget(std::string& name_or_number);

			/**
			 * @brief 判断该表格中是否存在这个天体
			 * @param name_or_number 名称或编号
			 * @return 查询到这个天体则返回true,否则返回false
			 */
			bool tatgetInTable(std::string& name_or_number);

			/**
			 * @brief 生成自动寻星的图像界面
			 * @param width 图像宽度
			 * @param height 图像高度
			 * @return 包含天体位置、中天位置的图像界面
			 */
			cv::Mat getTargetTableImage();

			/**
			 * @brief 用于找到鼠标就近的一个星点
			 */
			lsx::astronomy::ObservationTarget findCloseTarget(double& image_x, double& image_y, double& radius, bool* have_choose);

			/**
			 * @brief 从图像坐标转换到天体实际坐标
			 */
			void get_ra_dec(double& image_x, double& image_y, double* ra, double* dec);

			/**
			 * @brief 从赤经赤纬得到图像坐标
			 */
			void get_image_x_y(double& ra, double& dec, double* image_x, double* image_y);

			ObservationTable();

		private:
			cv::Mat _TargetTableImage;

			/**
			 * @brief 通过名称或者编号得到天体
			 */
			lsx::astronomy::ObservationTarget getTarget(std::string& name_or_number);

			/**
			 * @brief 星表类初始化
			 */
			void input_data();
		};

对应.cpp代码

void lsx::astronomy::ObservationTable::input_new_target(std::string number, std::string name, std::string showName, \
	double RA_hour, double RA_min, double RA_second, double DEC_degree, double DEC_min, double DEC_second, \
	bool is_star) {
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget(number, name, showName, RA_hour, RA_min, RA_second, DEC_degree, DEC_min, DEC_second, is_star));
}

lsx::astronomy::ObservationTable::ObservationTable() {
	this->_TargetTableImage = cv::Mat(cv::Size(goto_image_width, goto_image_height), CV_8UC3, background);
	cv::Point2d left_mid, right_mid, mid_up, mid_down, ra_gap1, ra_gap2, dec_gap1, dec_gap2;
	std::string coordinate_font_[4] = { "RA(0 h)","RA(24 h)","Dec(90 deg)", "Dec(-90 deg)" };
	cv::Size font_size[4];
	double dec_length = 0, ra_length = 0, font_scale = 0.4;
	double this_dec_degree = 0, down_threhold = 0, up_threhold = 0;
	int image_down_threhold = 0, image_up_threhold = 0, coordinate_font_thickness = 1,
		font_type = cv::FONT_HERSHEY_SIMPLEX, baseLine = 0, gap = 2;
	for (int i = 0; i < 4; i++) {
		font_size[i] = cv::getTextSize(coordinate_font_[i], font_type, font_scale, coordinate_font_thickness, &baseLine);
	}
	this->input_data();
	dec_length = double(_TargetTableImage.rows) / 180.0;
	ra_length = double(_TargetTableImage.cols) / 360.0;
	left_mid = cv::Point2d(0, _TargetTableImage.rows / 2); right_mid = cv::Point2d(_TargetTableImage.cols, _TargetTableImage.rows / 2);
	mid_up = cv::Point2d(_TargetTableImage.cols / 2, 0); mid_down = cv::Point2d(_TargetTableImage.cols / 2, _TargetTableImage.rows);
	for (int i = 1; i < 18; i++) {
		ra_gap1.x = i * double(goto_image_width) / 18.0; ra_gap1.y = 0;
		ra_gap2.x = ra_gap1.x; ra_gap2.y = _TargetTableImage.rows;
		cv::line(_TargetTableImage, ra_gap1, ra_gap2, background_line, 1);
	}
	for (int i = 1; i < 9; i++) {
		dec_gap1.x = 0; dec_gap1.y = i * double(goto_image_height) / 9.0;
		dec_gap2.x = _TargetTableImage.cols; dec_gap2.y = dec_gap1.y;
		cv::line(_TargetTableImage, dec_gap1, dec_gap2, background_line, 1);
	}
	left_mid.x += 2; left_mid.y -= double(goto_image_height) / 160.0; right_mid.y -= double(goto_image_height) / 160.0; right_mid.x -= double(goto_image_height) * 3.0 / 20.0;
	mid_up.x += double(goto_image_height) / 160.0; mid_down.x += double(goto_image_height) / 400.0; mid_up.y += double(goto_image_height) / 70.0; mid_down.y -= double(goto_image_height) / 60.0;
	cv::putText(this->_TargetTableImage, coordinate_font_[0], cv::Point(gap, 0.5 * this->_TargetTableImage.rows - gap),
		font_type, font_scale, big_word, coordinate_font_thickness, cv::LINE_AA);
	cv::putText(this->_TargetTableImage, coordinate_font_[1], cv::Point(this->_TargetTableImage.cols - 2.0 * font_size[1].width - gap, 0.5 * this->_TargetTableImage.rows - gap),
		font_type, font_scale, big_word, coordinate_font_thickness, cv::LINE_AA);
	cv::putText(this->_TargetTableImage, coordinate_font_[2], cv::Point(0.5 * this->_TargetTableImage.cols + gap, gap + font_size[2].height),
		font_type, font_scale, big_word, coordinate_font_thickness, cv::LINE_AA);
	cv::putText(this->_TargetTableImage, coordinate_font_[3], cv::Point(0.5 * double(this->_TargetTableImage.cols) + gap, double(this->_TargetTableImage.rows) - double(gap) - 0.5 * font_size[3].height),
		font_type, font_scale, big_word, coordinate_font_thickness, cv::LINE_AA);
	for (int i = 0; i < this->TargetTable.size(); i++) {
		this->TargetTable[i].position_in_image_x = this->TargetTable[i].RA_angle * ra_length;
		this->TargetTable[i].position_in_image_y = double(this->_TargetTableImage.rows) - (this->TargetTable[i].DEC_angle + 90.0) * dec_length;
		if (this->TargetTable[i].isStar) {
			lsx::starProcessing::DrawStar(this->_TargetTableImage, this->TargetTable[i].position_in_image_x, \
				this->TargetTable[i].position_in_image_y, star_color, 10);
			if (this->TargetTable[i].origin_show) {
				cv::putText(this->_TargetTableImage, this->TargetTable[i].ShowName, cv::Point(this->TargetTable[i].position_in_image_x + 15.0, this->TargetTable[i].position_in_image_y + 4.0), \
					cv::FONT_HERSHEY_SIMPLEX, 0.4, star_color, 1, cv::LINE_AA);
			}
		}
		else {
			cv::circle(this->_TargetTableImage, cv::Point(this->TargetTable[i].position_in_image_x, this->TargetTable[i].position_in_image_y), \
				4, not_star_color, -1, cv::LINE_AA);
			if (this->TargetTable[i].origin_show) {
				cv::putText(this->_TargetTableImage, this->TargetTable[i].ShowName, cv::Point(this->TargetTable[i].position_in_image_x + 8.0, this->TargetTable[i].position_in_image_y + 4.0), \
					cv::FONT_HERSHEY_SIMPLEX, 0.4, not_star_color, 1, cv::LINE_AA);
			}
		}
	}
}

//cv::Mat lsx::astronomy::ObservationTable::getTargetTableImage(int width, int height) {
//	cv::Mat targetTableImage = cv::Mat(cv::Size(width, height), CV_8UC3, background);
//	cv::Point2d left_mid, right_mid, mid_up, mid_down, zenith_up, zenith_down, ra_gap1, ra_gap2, dec_gap1, dec_gap2;
//	double dec_length = 0, ra_length = 0;
//	double this_dec_degree = 0, down_threhold = 0, up_threhold = 0;
//	int image_down_threhold = 0, image_up_threhold = 0;
//	dec_length = double(targetTableImage.rows) / 180.0;
//	ra_length = double(targetTableImage.cols) / 360.0;
//	left_mid = cv::Point2d(0, targetTableImage.rows / 2); right_mid = cv::Point2d(targetTableImage.cols, targetTableImage.rows / 2);
//	mid_up = cv::Point2d(targetTableImage.cols / 2, 0); mid_down = cv::Point2d(targetTableImage.cols / 2, targetTableImage.rows);
//	zenith_up.x = local_sidreal_time_degree() * ra_length;
//	zenith_down.x = zenith_up.x;
//	zenith_up.y = 0;
//	zenith_down.y = targetTableImage.rows;
//	for (int i = 1; i < 18; i++) {
//		ra_gap1.x = i * double(width) / 18.0; ra_gap1.y = 0;
//		ra_gap2.x = ra_gap1.x; ra_gap2.y = targetTableImage.rows;
//		cv::line(targetTableImage, ra_gap1, ra_gap2, background_line, 1);
//	}
//	for (int i = 1; i < 9; i++) {
//		dec_gap1.x = 0; dec_gap1.y = i * double(height) / 9.0;
//		dec_gap2.x = targetTableImage.cols; dec_gap2.y = dec_gap1.y;
//		cv::line(targetTableImage, dec_gap1, dec_gap2, background_line, 1);
//	}
//	cv::line(targetTableImage, zenith_up, zenith_down, cv::Scalar(zenith_line_color[0], zenith_line_color[1], zenith_line_color[2]), 1);
//	left_mid.x += 2; left_mid.y -= double(height) / 160.0; right_mid.y -= double(height) / 160.0; right_mid.x -= double(height) * 3.0 / 20.0;
//	mid_up.x += double(height) / 160.0; mid_down.x += double(height) / 400.0; mid_up.y += double(height) / 70.0; mid_down.y -= double(height) / 60.0;
//	zenith_down.x += double(height) / 400.0;zenith_down.y -= double(height) / 20.0;
//	cv::putText(targetTableImage, "RA 0h", left_mid, cv::FONT_HERSHEY_SIMPLEX, 0.4, big_word, 1, cv::LINE_AA);
//	cv::putText(targetTableImage, "RA 24h", right_mid, cv::FONT_HERSHEY_SIMPLEX, 0.4, big_word, 1, cv::LINE_AA);
//	cv::putText(targetTableImage, "DEC -90", mid_down, cv::FONT_HERSHEY_SIMPLEX, 0.4, big_word, 1, cv::LINE_AA);
//	cv::putText(targetTableImage, "DEC 90", mid_up, cv::FONT_HERSHEY_SIMPLEX, 0.4, big_word, 1, cv::LINE_AA);
//	cv::putText(targetTableImage, "Zenith", zenith_down, cv::FONT_HERSHEY_SIMPLEX, 0.4, big_word, 1, cv::LINE_AA);
//	for (int i = 0; i < this->TargetTable.size(); i++) {
//		this->TargetTable[i].position_in_image_x = this->TargetTable[i].RA_angle * ra_length;
//		this->TargetTable[i].position_in_image_y = double(targetTableImage.rows) - (this->TargetTable[i].DEC_angle + 90.0) * dec_length;
//		if (this->TargetTable[i].isStar) {
//			lsx::starProcessing::DrawStar(targetTableImage, this->TargetTable[i].position_in_image_x, \
//				this->TargetTable[i].position_in_image_y, star_color, 10);
//			cv::putText(targetTableImage, this->TargetTable[i].ShowName, cv::Point(this->TargetTable[i].position_in_image_x + 15.0, this->TargetTable[i].position_in_image_y + 4.0), \
//				cv::FONT_HERSHEY_SIMPLEX, 0.4, star_color, 1, cv::LINE_AA);
//		}
//		else {
//			cv::circle(targetTableImage, cv::Point(this->TargetTable[i].position_in_image_x, this->TargetTable[i].position_in_image_y), \
//				4, not_star_color, -1, cv::LINE_AA);
//			cv::putText(targetTableImage, this->TargetTable[i].ShowName, cv::Point(this->TargetTable[i].position_in_image_x + 8.0, this->TargetTable[i].position_in_image_y + 4.0), \
//				cv::FONT_HERSHEY_SIMPLEX, 0.4, not_star_color, 1, cv::LINE_AA);
//		}
//	}
//	for (int i = 0; i < targetTableImage.rows; i++) {
//		this_dec_degree = 90.0 - i / dec_length;
//		lsx::astronomy::theta_on_horizon_degree(this_dec_degree, &down_threhold, &up_threhold);
//		if (this_dec_degree <= 90.0 - global_latitude && this_dec_degree >= -(90.0 - global_latitude)) {
//			image_down_threhold = down_threhold * ra_length;
//			image_up_threhold = up_threhold * ra_length;
//			cv::Vec3b* pixelPtr = targetTableImage.ptr<cv::Vec3b>(i);
//			pixelPtr[image_down_threhold][0] = zenith_line_color[0];
//			pixelPtr[image_down_threhold][1] = zenith_line_color[1];
//			pixelPtr[image_down_threhold][2] = zenith_line_color[2];
//			pixelPtr[image_up_threhold] = pixelPtr[image_down_threhold];
//		}
//		
//	}
//	this->_TargetTableImage = targetTableImage;
//	return targetTableImage;
//}

cv::Mat lsx::astronomy::ObservationTable::getTargetTableImage() {
	cv::Mat targetTableImage;
	cv::Point2d zenith_up, zenith_down, ra_gap1;
	double dec_length = 0, ra_length = 0;
	double this_dec_degree = 0, down_threhold = 0, up_threhold = 0;
	int image_down_threhold = 0, image_up_threhold = 0;
	this->_TargetTableImage.copyTo(targetTableImage);
	dec_length = double(targetTableImage.rows) / 180.0;
	ra_length = double(targetTableImage.cols) / 360.0;
	zenith_up.x = local_sidreal_time_degree() * ra_length;
	zenith_down.x = zenith_up.x;
	zenith_up.y = 0;
	zenith_down.y = targetTableImage.rows;
	cv::line(targetTableImage, zenith_up, zenith_down, zenith_str_line, 1, cv::LINE_AA);
	zenith_down.x += double(goto_image_height) / 400.0; zenith_down.y -= double(goto_image_height) / 20.0;
	cv::putText(targetTableImage, "Zenith", zenith_down, cv::FONT_HERSHEY_SIMPLEX, 0.4, big_word, 1, cv::LINE_AA);
	for (int i = 0; i < targetTableImage.rows; i++) {
		this_dec_degree = 90.0 - i / dec_length;
		lsx::astronomy::theta_on_horizon_degree(this_dec_degree, &down_threhold, &up_threhold);
		if (this_dec_degree <= 90.0 - global_latitude && this_dec_degree >= -(90.0 - global_latitude)) {
			image_down_threhold = down_threhold * ra_length;
			image_up_threhold = up_threhold * ra_length;
			cv::Vec3b* pixelPtr = targetTableImage.ptr<cv::Vec3b>(i);
			pixelPtr[image_down_threhold][0] = zenith_line_color[0];
			pixelPtr[image_down_threhold][1] = zenith_line_color[1];
			pixelPtr[image_down_threhold][2] = zenith_line_color[2];
			pixelPtr[image_up_threhold] = pixelPtr[image_down_threhold];
		}
	}
	return targetTableImage;
}

void lsx::astronomy::ObservationTable::get_ra_dec(double& image_x, double& image_y, double* ra, double* dec) {
	double ra_length = double(this->_TargetTableImage.cols) / 360.0, dec_length = double(this->_TargetTableImage.rows) / 180.0;
	(*ra) = image_x / ra_length;
	(*dec) = 90.0 - image_y / dec_length;
}

void lsx::astronomy::ObservationTable::get_image_x_y(double& ra, double& dec, double* image_x, double* image_y) {
	double dec_length = 0, ra_length = 0;
	dec_length = double(this->_TargetTableImage.rows) / 180.0;
	ra_length = double(this->_TargetTableImage.cols) / 360.0;
	(*image_x) = ra * ra_length;
	(*image_y) = double(this->_TargetTableImage.rows) - (dec + 90.0) * dec_length;
}

lsx::astronomy::ObservationTarget lsx::astronomy::ObservationTable::findCloseTarget(double& image_x, double& image_y, double& radius, bool* have_choose) {
	int i = 0, min_length = 2000000, this_length = 0, close_i = 0;
	for (i = 0; i < this->TargetTable.size(); i++) {
		this_length = lsx::math::PointLength(image_x, image_y, this->TargetTable[i].position_in_image_x, this->TargetTable[i].position_in_image_y);
		if (this_length < min_length) {
			close_i = i;
			min_length = this_length;
		}
	}
	if (min_length < radius) {
		(*have_choose) = true;
		return this->TargetTable[close_i];
	}
	else {
		(*have_choose) = false;
		return lsx::astronomy::ObservationTarget::ObservationTarget();
	}
}

bool lsx::astronomy::ObservationTable::tatgetInTable(std::string& name_or_number) {
	for (int i = 0; i < this->TargetTable.size(); i++) {
		if (this->TargetTable[i].isName(name_or_number)) {
			return true;
		}
	}
	return false;
}

lsx::astronomy::ObservationTarget lsx::astronomy::ObservationTable::getTarget(std::string& name_or_number) {
	for (int i = 0; i < this->TargetTable.size(); i++) {
		if (this->TargetTable[i].isName(name_or_number)) {
			return this->TargetTable[i];
		}
	}
}

lsx::astronomy::ObservationTarget lsx::astronomy::ObservationTable::findTarget(std::string& name_or_number) {
	if (this->tatgetInTable(name_or_number)) {
		return this->getTarget(name_or_number);
	}
	else {
		return lsx::astronomy::ObservationTarget();
	}
}

void lsx::astronomy::ObservationTable::input_data() {

	/*-----------------------------------------------------------天体---------------------------------------------------------------*/

	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M42", "M42(Great Orion Nebula)", "M42", 5.0, 36.0, 27.6, -5.0, -22.0, -58.4, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M31", "M31(Andromeda Galaxy)", "M31", 0, 44.0, 5.7, 41.0, 23.0, 37.4, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M20", "M20(Trifid Nebula)", "M20", 18.0, 3.0, 58.1, -22.0, -57.0, -55.3, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M33", "M33(Triangulum Galaxy)", "M33", 1.0, 35.0, 15.0, 30.0, 46.0, 50.9, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M45", "M45(Pleiades)", "M45", 3.0, 48.0, 22.9, 24.0, 13.0, 30.8, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("IC434", "IC434(Horsehead Nebula)", "IC434", 5.0, 42.0, 27.8, -2.0, -28.0, -55.7, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M101", "M101(Pinwheel Galaxy)", "M101", 14.0, 3.0, 55.8, 54.0, 13.0, 27.4, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M86", "M86(Markarian's Chain)", "Markarian's Chain", 12.0, 30.0, 1.8, 12.0, 57.0, 7.8, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M1", "M1(Crab Nebula)", "M1", 5.0, 35.0, 59.8, 22.0, 1.0, 56.7, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M8", "M8(Lagoon Nebula)", "M8", 18.0, 5.0, 19.7, -24.0, -21.0, -37.3, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M81", "M81(Bode's Nebula)", "M81", 9.0, 57.0, 34.9, 68.0, 56.0, 59.8, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("NGC2237", "NGC2237(Rosette Nebula)", "NGC2237", 6.0, 33.0, 10.3, 5.0, 0.0, 54.5, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M51", "M51(Whirlpool Galaxy)", "M51", 13.0, 30.0, 54.6, 47.0, 3.0, 57.7, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M106", "M106(Intermediate Spiral Seyfert 2 Galaxy)", "M106", 12.0, 20.0, 10.4, 47.0, 9.0, 57.8, false, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("NGC4217", "NGC4217(Spiral Galaxy)", "NGC4217", 12.0, 17.0, 4.2, 46.0, 57.0, 14.6, false, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M104", "M104(Sombrero Galaxy)", "M104", 12.0, 41.0, 15.1, -11.0, -45.0, -21.3, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("NGC4565", "NGC4565(Needle Galaxy)", "NGC4565", 12.0, 37.0, 33.3, 25.0, 51.0, 5.5, false));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("M4", "M4(Globular Cluster)", "M4", 16.0, 25.0, 3.1, -26.0, -34.0, -53.1, false, false));

	/*-------------------------------------------------------------恒星-------------------------------------------------------------*/

	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("大角星", "大角星(Arcturus)", "Da Jiao", 14.0, 16.0, 45.9, 19.0, 3.0, 12.4, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("织女星", "织女星(Vega)", "Zhi Nv", 18.0, 37.0, 43.7, 38.0, 48.0, 5.2, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("河鼓二", "河鼓二(Altair)", "He Gu 2", 19.0, 51.0, 56.0, 8.0, 55.0, 45.3, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("天津四", "天津四(Deneb)", "Tian Jin 4", 20.0, 42.0, 12.8, 45.0, 21.0, 49.3, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("心宿二", "心宿二(Antares)", "Xin Xiu 2", 16.0, 30.0, 52.4, -26.0, -29.0, -4.8, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("天狼星", "天狼星(Sirius)", "Tian Lang", 6.0, 46.0, 13.5, -16.0, -45.0, -3.7, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("角宿一", "角宿一(Spica)", "Jiao Xiu 1", 13.0, 26.0, 28.2, -11.0, -17.0, -15.2, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("参宿七", "参宿七(Rigel)", "Sen Xiu 7", 5.0, 15.0, 42.3, -8.0, -10.0, -32.2, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("参宿四", "参宿四(Betelgeuse)", "Sen Xiu 4", 5.0, 56.0, 29.3, 7.0, 24.0, 38.8, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("五车二", "五车二(Capella)", "Wu Che 2", 5.0, 18.0, 29.0, 46.0, 1.0, 27.4, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("南河三", "南河三(Procyon)", "Nan He 3", 7.0, 40.0, 34.7, 5.0, 9.0, 43.9, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("奎宿九", "奎宿九(Mirach)", "kui Xiu 9", 1.0, 11.0, 3.9, 35.0, 44.0, 58.1, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("北斗三", "北斗三(Phad)", "Bei Dou 3", 11.0, 55.0, 7.5, 53.0, 33.0, 25.5, true));
	this->TargetTable.push_back(lsx::astronomy::ObservationTarget::ObservationTarget("北斗七", "北斗七(Alkaid)", "Bei Dou 7", 13.0, 48.0, 30.4, 49.0, 11.0, 15.7, true));

	/*-------------------------------特殊观测目标------------------------------------*/
}

类AstroTimer

        这个类使用IAU1982标准,用于计算当地恒星时,为系统系统时间坐标参数。

头文件:

		/**
		 * @brief 当地恒星时计算器
		 */
		class AstroTimer {
		public:

			/**
			 * @brief 计算当地恒星时
			 * @param none
			 * @return void
			 */
			double get_local_sidereal_time();

		private:

			unsigned int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0;

			/**
			 * @brief 计算系统当前时间
			 * @param none
			 * @return void
			 */
			void get_this_time();

			/**
			 * @brief 计算(0h UT)儒略日,使用星图软件stellarium计算方法,
			          计算方式来源:https://blog.csdn.net/shaoguangleo/article/details/6248315
			 * @param none
			 * @return 返回的儒略日(long double类型)
			 */
			long double JD();

			/**
			 * @brief 计算任意时刻的儒略日,使用星图软件stellarium计算方法,
				      计算方式来源:https://blog.csdn.net/shaoguangleo/article/details/6248315
			 * @param none
			 * @return 返回的儒略日(long double类型)
			 */
			long double _JD();

			/**
			 * @brief 得到中介参数T(T = (JD - 2451545.0) / 36525)
			 * @param is_0h_UT 是否是计算(0h UT)标志位
			 * @return 中介参数T
			 */
			long double T(bool is_0h_UT = true);

			/**
			 * @brief 格林尼治平恒星时(使用IAU1982标准)
			 * @param is_0h_UT 是否是计算(0h UT)标志位,当该位为true时计算格林尼治(0h UT)的平恒星时
			                   ,当该位为false时计算任意时刻的格林尼治平恒星时
			 * @return 格林尼治平恒星时
			 */
			long double Greenwich_mean_sidereal_time(bool is_0h_UT = true);
		};

源文件:

void lsx::astronomy::AstroTimer::get_this_time() {
	SYSTEMTIME time_;
	GetLocalTime(&time_);
	this->year = time_.wYear;
	this->month = time_.wMonth;
	this->day = time_.wDay;
	this->hour = time_.wHour;
	this->min = time_.wMinute;
	this->sec = time_.wSecond;
}

long double lsx::astronomy::AstroTimer::_JD() {
	int y, m, B;
	this->get_this_time();
	y = this->year;
	m = this->month;
	if (this->month <= 2)
	{
		y = this->year - 1;
		m = this->month + 12;
	}
	B = -2;
	if (this->year > 1582 || (this->year == 1582 && (this->month > 10 || (this->month == 10 && this->day >= 15))))
	{
		B = y / 400 - y / 100;
	}
	return (floor(365.25 * y) + floor(30.6001 * (double(m) + 1.0)) + B + 1720996.5 + this->day + this->hour / 24.0 + this->min / 1440.0 + this->sec / 86400.0);
}

long double lsx::astronomy::AstroTimer::JD() {
	int y, m, B;
	this->get_this_time();
	y = this->year;
	m = this->month;
	if (this->month <= 2)
	{
		y = this->year - 1;
		m = this->month + 12;
	}
	B = -2;
	if (this->year > 1582 || (this->year == 1582 && (this->month > 10 || (this->month == 10 && this->day >= 15))))
	{
		B = y / 400 - y / 100;
	}
	return (floor(365.25 * y) + floor(30.6001 * (double(m) + 1.0)) + B + 1720996.5 + this->day);
}

long double lsx::astronomy::AstroTimer::T(bool is_0h_UT) {
	if (is_0h_UT) {
		return (this->JD() - long double(2451545.0)) / long double(36525.0);
	}
	else {
		return (this->_JD() - long double(2451545.0)) / long double(36525.0);
	}
}

long double lsx::astronomy::AstroTimer::Greenwich_mean_sidereal_time(bool is_0h_UT) {
	long double this_T = this->T(is_0h_UT), greenwich_mean_sidereal_time = 0;
	long double T = 0;
	if (is_0h_UT) {
		greenwich_mean_sidereal_time = long double(100.46061837) +
			long double(36000.770053608) * this_T +
			long double(0.000387933) * this_T * this_T -
			this_T * this_T * this_T / long double(38710000);
	}
	else {
		greenwich_mean_sidereal_time = long double(280.46061837) +
			long double(360.98564736629) * (this->_JD() - 2451545.0) +
			long double(0.000387933) * this_T * this_T -
			this_T * this_T * this_T / long double(38710000);
	}
	return greenwich_mean_sidereal_time;
}

double lsx::astronomy::AstroTimer::get_local_sidereal_time() {
	double output_local_sidereal_time = 0;
	double local_time = 0;//地方平时
	double greenwich_time = 0;//格林尼治平时
	this->get_this_time();
	local_time = lsx::astronomy::ra2degree(this->hour, this->min, this->sec) + global_longitude - lsx::astronomy::ra2degree(8, 0, 0);//地方平时
	greenwich_time = local_time - global_longitude;//格林尼治平时
	output_local_sidereal_time = local_time + double(this->Greenwich_mean_sidereal_time(true)) + greenwich_time / 365.2422;
	output_local_sidereal_time = lsx::astronomy::RA_inRegion_0_360(output_local_sidereal_time);
	return output_local_sidereal_time;
}

命名空间astronomy中其他函数

        这些函数包括论文里面将角度转换到0 - 360之间的函数,赤道坐标-地平坐标转换函数和一些角度换算函数。

头文件:

		/**
		 * @brief 得到星点指向向量
		 * @param ra_degree 赤经,单位:°
		 * @param dec_degree 赤经,单位:°
		 * @param zenith_degree 中天赤经:单位:°
		 * @return 星点在天体坐标系下的指向向量
		 */
		Eigen::MatrixXd StarPointVector(double& ra_degree, double& dec_degree, double& zenith_degree);

		/**
		 * @brief 将赤经角度转换到0~360°区间内
		 * @param inputRA 输入的角度,单位:°
		 * @return 转换后的角度
		 */
		double RA_inRegion_0_360(double inputRA);

		/**
		 * @brief 得到中天位置的假想天体
		 */
		lsx::astronomy::ObservationTarget GetZenith();

		/**
		 * @brief 两个天体之间的最短赤经角距离
		 * @param inputRA1 输入的天体1赤经,单位:°
		 * @param inputRA2 输入的天体2赤经,单位:°
		 * @return 最短角距离,单位:°
		 */
		double getMinAngle(double& inputRA1, double& inputRA2);

		/**
		 * @brief 两个天体之间的最短赤经角距离
		 * @param inputTarget1 输入的天体1
		 * @param inputTarget2 输入的天体2
		 * @return 最短角距离,单位:°
		 */
		double getMinAngle(lsx::astronomy::ObservationTarget& inputTarget1, lsx::astronomy::ObservationTarget& inputTarget2);

		/**
		 * @brief 判断天体是否通过中天
		 * @param inputarget 输入天体
		 * @return 通过中天则为true,否则为false
		 */
		bool RA_PassZenith(lsx::astronomy::ObservationTarget& inputTarget);

		/**
		 * @brief 判断天体是否在地平线以下
		 * @param inputOberveTarget 输入天体
		 * @return 在地平线以下为true,否则为false
		 */
		bool TargetUnderHorizon(lsx::astronomy::ObservationTarget& inputOberveTarget);

		/**
		 * @brief 将天体的赤道坐标转换为地平坐标,并保存在对应的成员变量其中
		 * @param inputTarget 输入天体
		 * @return void
		 */
		void Equ2EleAzi(lsx::astronomy::ObservationTarget* inputTarget);

		/**
		 * @brief 计算地球运行角度
		 * @param inputTime_ms 输入的运行时间,单位:ms
		 * @return 地球运行的角度,单位:°
		 */
		double earthRotateAngle(double inputTime_ms);

		/**
		 * @brief 计算地球转动时间
		 * @param inputAngle_degree 输入的运行角度,单位:°
		 * @return 地球转动的时间,单位:ms
		 */
		int earthRotateTime(double inputAngle_degree);

		/**
		 * @brief 赤经坐标转换为角度
		 * @param hour 赤经时
		 * @param min 赤经分
		 * @param sec 赤经秒
		 * @return RA角度,单位:°
		 */
		double ra2degree(double hour, double min, double sec);

		/**
		 * @brief 赤纬坐标转换为角度
		 * @param degree 赤纬度
		 * @param min 赤纬分
		 * @param sec 赤纬秒
		 * @return DEC角度,单位:°
		 */
		double dec2degree(double degree, double min, double sec);

		/**
		 * @brief 赤纬坐标转换为时间
		 * @param inputAngle_degree 输入的角度
		 * @param ra_hour 赤经时
		 * @param ra_min 赤经分
		 * @param ra_sec 赤经秒
		 * @return void
		*/
		void degree2ra(double inputAngle_degree, double* ra_hour, double* ra_min, double* ra_sec);

		/**
		 * @brief 将角度用度、分、秒表示
		 * @param input_angle_degree 输入的角度
		 * @param deg 度
		 * @param min 分
		 * @param sec 秒
		 * @return void
		*/
		void degree_2_deg_min_sec(double input_angle_degree, double* deg, double* min, double* sec);

		/**
		 * @brief 将赤经角度转换到0~360°区间内
		 * @param inputAngle_degree 输入的角度,单位:°
		 * @return 转换后的角度,单位:°
		 */
		double angle_in_0_360(double inputAngle_degree);

		/**
		 * @brief 得到地平线上的赤经范围
		 * @param dec_angle_degree 天体的赤纬坐标
		 * @param down_threhold 地平线上天体下阈值
		 * @param up_threhold 地平线上天体上阈值
		 * @return 角度范围
		 */
		double theta_on_horizon_degree(double dec_angle_degree, double* down_threhold, double* up_threhold);

源文件:

bool lsx::astronomy::TargetUnderHorizon(lsx::astronomy::ObservationTarget& inputOberveTarget) {
	double gama = acos(tan(inputOberveTarget.DEC_angle / 180.0 * PI) * tan(global_latitude / 180.0 * PI));
	lsx::astronomy::ObservationTarget zenithTarget;
	double theta = 0;
	zenithTarget = lsx::astronomy::GetZenith();
	if (inputOberveTarget.DEC_angle > 90.0 - global_latitude) {
		return false;
	}
	if (inputOberveTarget.DEC_angle > 0.0 && inputOberveTarget.DEC_angle < 90.0 - global_latitude) {
		theta = 360.0 - 2.0 * gama;
	}
	else if (inputOberveTarget.DEC_angle < 0.0 && inputOberveTarget.DEC_angle > -(90.0 - global_latitude)) {
		theta = 2.0 * gama;
	}
	else {
		return true;
	}
	if (lsx::astronomy::getMinAngle(inputOberveTarget, zenithTarget) < 0.5 * theta) {
		return false;
	}
	return true;
}

Eigen::MatrixXd lsx::astronomy::StarPointVector(double& ra_degree, double& dec_degree, double& zenith_degree) {
	return lsx::math::StarPointVector3(ra_degree / 180.0 * PI, dec_degree / 180.0 * PI, zenith_degree / 180.0 * PI);
}

void lsx::astronomy::Equ2EleAzi(lsx::astronomy::ObservationTarget* inputTarget) {
	Eigen::MatrixXd star_vector(3, 1), z_axis(3, 1), _x_axis(2, 1), star_vector_floor(2, 1);
	double azimuth = 0;
	z_axis << 0, 0, 1;
	_x_axis << -1, 0;
	star_vector = lsx::math::StarPointVector3(inputTarget->RA_angle / 180.0 * PI,
		inputTarget->DEC_angle / 180.0 * PI,
		local_sidreal_time_degree() / 180.0 * PI);
	inputTarget->elevation = (0.5 * PI - lsx::math::VectorAngle_rad(star_vector, z_axis)) /
		PI * 180.0 ;
	star_vector_floor << star_vector(0, 0), star_vector(1, 0);
	azimuth = lsx::math::VectorAngle_rad(star_vector_floor, _x_axis) / PI * 180.0;
	if (star_vector(1, 0) > 0) {
		inputTarget->azimuth = azimuth;
	}
	else {
		inputTarget->azimuth = 360.0 - azimuth;//加减360°是等效的
	}
}

double lsx::astronomy::earthRotateAngle(double inputTime_ms) {
	return 360.0 * inputTime_ms / (86164.0 * 1000.0);
}

int lsx::astronomy::earthRotateTime(double inputAngle_degree) {
	return int(inputAngle_degree * 1000.0 * 86164.0 / 360.0);
}

double lsx::astronomy::ra2degree(double hour, double min, double sec) {
	return 15.0 * hour + 0.25 * min + sec / 240.0;
}

double lsx::astronomy::dec2degree(double degree, double min, double sec) {
	return degree + min / 60.0 + sec / 3600.0;
}

void lsx::astronomy::degree2ra(double inputAngle_degree, double* ra_hour, double* ra_min, double* ra_sec) {
	double stay_angle = 0;
	inputAngle_degree = lsx::astronomy::angle_in_0_360(inputAngle_degree);
	stay_angle = inputAngle_degree / 15.0;
	(*ra_hour) = int(stay_angle);
	inputAngle_degree -= int(stay_angle) * 15.0;
	stay_angle = inputAngle_degree / 0.25;
	(*ra_min) = int(stay_angle);
	inputAngle_degree -= int(stay_angle) * 0.25;
	stay_angle = inputAngle_degree / (0.25 / 60);
	(*ra_sec) = stay_angle;
}

void lsx::astronomy::degree_2_deg_min_sec(double input_angle_degree, double* deg, double* min, double* sec) {
	double _deg = 0, _min = 0, _sec = 0, angle = fabs(input_angle_degree);
	_deg = floor(angle);
	angle = (angle - _deg) * 60.0;
	_min = floor(angle);
	angle = (angle - _min) * 60.0;
	_sec = angle;
	if (input_angle_degree < 0) {
		_deg *= -1; _min *= -1; _sec *= -1;
	}
	*deg = _deg; *min = _min; *sec = _sec;
}

double lsx::astronomy::angle_in_0_360(double inputAngle_degree) {
	if (inputAngle_degree < 0) {
		while (inputAngle_degree < 0) {
			inputAngle_degree += 360.0;
		}
	}
	else if (inputAngle_degree > 360.0) {
		while (inputAngle_degree > 360.0) {
			inputAngle_degree -= 360.0;
		}
	}
	return inputAngle_degree;
}

至此自动寻星需要的类介绍完成。

自动寻星的实现

        懒得写了,这里直接介绍类AutoStarFindController。

头文件:

		class AutoStarFindController {
		public:

			/**
			 * @brief 自动寻星系统函数
			 */
			void AutomaticStarFinding();

			/**
			 * @brief 预先设置自动寻星控制器,将初始位置调整为当前指向
			 * @param targetName 当前赤道仪指向天体名称
			 * @param targetNumber 当前赤道仪指向天体编号
			 * @param decSide Dec转轴半盘位置,可选:DEC_IS_ON_THE_LEFT;DEC_IS_ON_THE_RIGHT
			 * @param afterGuide 导星结束标志位
			 * @return void
			 */
			void preSet(std::string targetName = "empty target", std::string targetNumber = "empty target", int decSide = DEC_IS_ON_THE_RIGHT);
			
		private:
			lsx::astronomy::ObservationTable targetTable;
			lsx::astronomy::ObservationTarget start, end;
			bool zero2star = false;//是否是从0位置开始的
			bool ra_arrival = false, dec_arrival = false;
			lsx::astronomy::ObservationTarget pointTarget;
			bool this_target_is_star = false;

			/**
			 * @brief 自动寻星系统主函数
			 */
			void gotoControl();

			///**
			// * @brief 判断天体是否在地平线之上
			// */
			//bool onHorizon(lsx::astronomy::ObservationTarget inputTarget);

			/**
			 * @brief 两天体之间的最小赤经角距离
			 */
			double ra_minAngle(lsx::astronomy::ObservationTarget target1, lsx::astronomy::ObservationTarget target2);

			/**
			 * @brief RA通过中天
			 */
			bool passZenith(lsx::astronomy::ObservationTarget inputTarget);

			/**
			 * @brief 将赤经转换在0-360之间
			 */
			double ra_in_region_0_360(double input_ra);

			///**
			// * @brief 显示延时倒计时
			// */
			//void showRunTime(double RA_inputTime_ms, double DEC_inputTime_ms, lsx::astronomy::ObservationTarget start, lsx::astronomy::ObservationTarget end);

			/**
			 * @brief 得到移动的赤道仪位置坐标
			 */
			void get_image_position(double start_x, double start_y, double end_x, double end_y, double x_runtime, double y_runtime, \
				std::clock_t start_time, std::clock_t end_time, \
				cv::Point* now);
		};

源文件:

#include"AutoStarSearch.h"

extern bool noTarget;//是否是第一次使用自动寻星
extern bool after_guide;//是否是在导星之后进入自动寻星
extern int DEC_side;//DEC运行半圆所在的方位
extern std::string this_target_name;//当前目标名称
extern std::string this_target_number;//当前观测目标编号(如M42等)

extern cv::Scalar coordinate_words;//鼠标触摸到天体后显示坐标的颜色
extern cv::Scalar show_state_1_color;//Select a star with the mouse to calibrate the '0' position.提示符颜色
extern cv::Scalar my_equatorial_instrument_color;//赤道仪图像显示的颜色
extern cv::Scalar my_equatorial_instrument_line_color;//赤道仪移动线颜色
extern cv::Scalar optional_target_color;//可选天体的图像显示颜色

extern int goto_image_width, goto_image_height;//自动寻星的图像大小

double mouse_botton_x = -1, mouse_botton_y = -1, mouse_touch_x = -1, mouse_touch_y = -1;
bool mouse_get_coondinate = false;
bool goto_origin_position = false;
bool deal_mouse_event = true;

void autoStarSearch_mouse_callback(int event_, int x, int y, int flags, void* userData) {
	if (deal_mouse_event) {
		if (event_ == cv::EVENT_LBUTTONDOWN) {
			mouse_botton_x = x;
			mouse_botton_y = y;
			mouse_get_coondinate = true;
			/*	std::cout << mouse_botton_x << " " << mouse_botton_y << std::endl;*/
		}
		else if (event_ == cv::EVENT_RBUTTONDOWN) {
			mouse_botton_x = x;
			mouse_botton_y = y;
			mouse_get_coondinate = true;
			goto_origin_position = true;
		}
	}
	if (event_ == cv::EVENT_MOUSEMOVE) {
		mouse_touch_x = x;
		mouse_touch_y = y;
	}
}

//bool lsx::autoSearch::AutoStarFindController::onHorizon(lsx::astronomy::ObservationTarget inputTarget) {
//	lsx::astronomy::ObservationTarget thisTarget = inputTarget;
//	lsx::astronomy::Equ2EleAzi(&thisTarget);
//	if (thisTarget.elevation > 0) {
//		return true;
//	}
//	else {
//		return false;
//	}
//}

double lsx::autoSearch::AutoStarFindController::ra_minAngle(lsx::astronomy::ObservationTarget target1, lsx::astronomy::ObservationTarget target2) {
	return lsx::math::Min(fabs(target1.RA_angle - target2.RA_angle), 360.0 - fabs(target1.RA_angle - target2.RA_angle));
}

bool lsx::autoSearch::AutoStarFindController::passZenith(lsx::astronomy::ObservationTarget inputTarget) {
	Eigen::MatrixXd zenith_vector(3, 1), target_vector(3, 1), z_axis(3, 1);
	zenith_vector << 0, -1, 0;
	z_axis << 0, 0, 1;
	target_vector = lsx::math::RotationMatrix3(z_axis, (inputTarget.RA_angle - local_sidreal_time_degree()) / 180.0 * PI) * zenith_vector;
	if (target_vector(0, 0) > 0) {
		return false;
	}
	else {
		return true;
	}
}

double lsx::autoSearch::AutoStarFindController::ra_in_region_0_360(double input_ra) {
	return lsx::astronomy::RA_inRegion_0_360(input_ra);
}

//void lsx::autoSearch::AutoStarFindController::showRunTime(double RA_inputTime_ms, double DEC_inputTime_ms, lsx::astronomy::ObservationTarget start, lsx::astronomy::ObservationTarget end) {
//	int RA_runtime_sec = RA_inputTime_ms / 1000.0 + 1, DEC_runtime_sec = DEC_inputTime_ms / 1000.0 + 1;
//	int max_runtime = lsx::math::Max(RA_runtime_sec, DEC_runtime_sec);
//	std::cout << std::endl;
//	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);
//	std::cout << " 起点:" << start.name << "(RA:" << start.RA_hour << " h " << start.RA_min << " min " << start.RA_second << " s , DEC:" \
//		<< start.DEC_degree << "°" << fabs(start.DEC_min) << " min " << fabs(start.DEC_second) << " sec)" << std::endl;
//	std::cout << std::endl;
//	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
//	std::cout << " 终点:" << end.name << "(RA:" << end.RA_hour << " h " << end.RA_min << " min " << end.RA_second << " s , DEC:" \
//		<< end.DEC_degree << "°" << fabs(end.DEC_min) << " min " << fabs(end.DEC_second) << " sec)" << std::endl;
//	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE);
//	std::cout << std::endl;
//	std::cout << " 赤道仪运行中. . ." << std::endl;
//	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
//	std::cout << std::endl;
//	for (int i = 0; i < max_runtime; i++) {
//		printf_s("\r");
//		if (RA_runtime_sec - i > 0 && DEC_runtime_sec - i > 0)
//			std::cout << " RA剩余时间:" << RA_runtime_sec - i << "秒 | DEC剩余时间:" << DEC_runtime_sec - i << "秒      ";
//		else if (RA_runtime_sec - i <= 0 && DEC_runtime_sec - i > 0)
//			std::cout << " RA运行结束!" << " | DEC剩余时间:" << DEC_runtime_sec - i << "秒              ";
//		else if (RA_runtime_sec - i > 0 && DEC_runtime_sec - i <= 0)
//			std::cout << " RA剩余时间:" << RA_runtime_sec - i << "秒 | DEC运行结束!                 ";
//		else
//			std::cout << "赤道仪到达目标位置!                                                       ";
//		Sleep(1000);
//	}
//	system("cls");
//}

/**
 * @brief 得到赤道仪的图像位置,时间单位都是毫秒
 */
void lsx::autoSearch::AutoStarFindController::get_image_position(double start_x, double start_y, double end_x, double end_y, double x_runtime, double y_runtime, \
	std::clock_t start_time, std::clock_t end_time, \
	cv::Point* now) {
	double k_y = (end_y - start_y) / y_runtime, k_x = (end_x - start_x) / x_runtime;
	double d_time = double(end_time) - double(start_time);
	now->x = k_x * d_time + start_x;
	now->y = k_y * d_time + start_y;
	if (start_x > end_x && start_y > end_y) {
		if (now->x <= end_x) {
			now->x = end_x;
		}
		if (now->y <= end_y) {
			now->y = end_y;
		}
	}
	else if (start_x <= end_x && start_y > end_y) {
		if (now->x >= end_x) {
			now->x = end_x;
		}
		if (now->y <= end_y) {
			now->y = end_y;
		}
	}
	else if (start_x > end_x && start_y <= end_y) {
		if (now->x <= end_x) {
			now->x = end_x;
		}
		if (now->y >= end_y) {
			now->y = end_y;
		}
	}
	else if (start_x <= end_x && start_y <= end_y) {
		if (now->x >= end_x) {
			now->x = end_x;
		}
		if (now->y >= end_y) {
			now->y = end_y;
		}
	}
}

void lsx::autoSearch::AutoStarFindController::gotoControl() {
	int dec_direction = 0, ra_direction = 0;
	double find_radius = 20; double ra_runtime_ms = 0, dec_runtime_ms = 0; 
	double ra_angle = 0, dec_angle = 0, dec_angle_part = 0;
	bool show_warning = false; bool arrive_the_target = false; bool choose_target = false; bool touch_target = false;
	bool get_end_target = false; bool back_origin_position = false;
	char choose_choose = 'n';
	std::string inputTargetName;
	cv::Mat star_image; cv::Point now_position;
	std::clock_t start_time, end_time;
	lsx::astronomy::ObservationTarget mouse_touch_target;//鼠标触摸到的目标
	double now_run_time = 0; double start_x = 0, start_y = 0, end_x = 0, end_y = 0; 
	double width = goto_image_width, heigth = goto_image_height;
	std::string win_name = "Shixuan Liu - Auto Star Search", _EQ = "my EQ";
	std::string show_target_name = " Name: ";
	char show_ra[50] = { 0 }, show_dec[50] = { 0 }, show_my_equ[90] = { 0 }, show_ele[50] = { 0 }, show_azi[50] = { 0 };
	double elevation_2_deg_min_sec[3] = { 0 }, azimuth_2_deg_min_sec[3] = { 0 };
	cv::namedWindow(win_name, cv::WINDOW_AUTOSIZE);
	//cv::setWindowProperty(win_name, cv::WND_PROP_FULLSCREEN, cv::WINDOW_FULLSCREEN);
	cv::setMouseCallback(win_name, autoStarSearch_mouse_callback);
	mouse_get_coondinate = false;
	goto_origin_position = false;
	deal_mouse_event = true;
	star_image = this->targetTable.getTargetTableImage();
	/*----------------------------------------------------目标天体输入---------------------------------------------------------*/
	if (noTarget) {
		this->zero2star = true;
	}
	/*----------------------------------------------------当前天体输入------------------------------------------------------------*/
	if (noTarget && !after_guide) {//从零位开始,没有经过导星
		this->start.RA_angle = this->ra_in_region_0_360(local_sidreal_time_degree() + 90.0);
		this->start.DEC_angle = 90.0;
		this->start.DEC_degree = 90.0;
		this->start.name = "零位";
		this->start.number = "0";
		this->start.ShowName = "E0";
		lsx::astronomy::degree2ra(this->start.RA_angle, &this->start.RA_hour, &this->start.RA_min, &this->start.RA_second);
	}
	else if (noTarget && after_guide) {//从零位模式开始,经过导星
		if (this->targetTable.tatgetInTable(this_target_name)) {
			this->start = this->targetTable.findTarget(this_target_name);
		}
		else {
			this->start = this->targetTable.findTarget(this_target_number);
		}
	}
	else {
		if (!(this_target_name == "零位" || this_target_number == "0")) {
			if (this->targetTable.tatgetInTable(this_target_name)) {
				this->start = this->targetTable.findTarget(this_target_name);
			}
			else {
				this->start = this->targetTable.findTarget(this_target_number);
			}
		}
		else {
			this->start.RA_angle = this->ra_in_region_0_360(local_sidreal_time_degree() + 90.0);
			this->start.DEC_angle = 90.0;
			this->start.DEC_degree = 90.0;
			this->start.name = "零位";
			this->start.number = "0";
			lsx::astronomy::degree2ra(this->start.RA_angle, &this->start.RA_hour, &this->start.RA_min, &this->start.RA_second);
		}
	}
	/*---------------------------------------------------------目标天体输入---------------------------------------------------*/
	get_end_target = false;
	while (!get_end_target) {
		/*-------------对生成图像进行初始化的代码段-------------------*/
		star_image = this->targetTable.getTargetTableImage();
		if (this->zero2star) {
			cv::putText(star_image, "Select a star with the mouse to calibrate the '0' position.", cv::Point(0 + double(heigth) / 200.0, 0 + double(heigth) / 50.0), \
				cv::FONT_HERSHEY_SIMPLEX, 0.6, show_state_1_color, 1, cv::LINE_AA);
		}
		else {
			cv::putText(star_image, "Select a observed object with the mouse.", cv::Point(0 + double(heigth) / 200.0, 0 + double(heigth) / 50.0), \
				cv::FONT_HERSHEY_SIMPLEX, 0.6, show_state_1_color, 1, cv::LINE_AA);
		}
		this->targetTable.get_image_x_y(this->start.RA_angle, this->start.DEC_angle, &this->start.position_in_image_x, &this->start.position_in_image_y);
		lsx::starProcessing::DrawRegularTriangle(star_image, this->start.position_in_image_x, this->start.position_in_image_y, my_equatorial_instrument_color, double(heigth) / 100.0);
		cv::circle(star_image, cv::Point(this->start.position_in_image_x, this->start.position_in_image_y), double(heigth) / 80.0, my_equatorial_instrument_color, 1, cv::LINE_AA);
		cv::putText(star_image, _EQ, cv::Point(this->start.position_in_image_x + double(heigth) / 60.0, this->start.position_in_image_y - double(heigth) / 60.0), \
			cv::FONT_HERSHEY_SIMPLEX, 0.4, my_equatorial_instrument_color, 1, cv::LINE_AA);
		/*---------------------*/
		if (mouse_touch_target.position_in_image_x > 0 && mouse_touch_target.position_in_image_y > 0) {//鼠标触摸显示
			lsx::astronomy::Equ2EleAzi(&mouse_touch_target);
			lsx::astronomy::degree_2_deg_min_sec(mouse_touch_target.elevation, &elevation_2_deg_min_sec[0], &elevation_2_deg_min_sec[1], &elevation_2_deg_min_sec[2]);
			lsx::astronomy::degree_2_deg_min_sec(mouse_touch_target.azimuth, &azimuth_2_deg_min_sec[0], &azimuth_2_deg_min_sec[1], &azimuth_2_deg_min_sec[2]);
			sprintf_s(show_ra, " RA: %d hour %d min %d sec", int(mouse_touch_target.RA_hour), int(mouse_touch_target.RA_min), int(mouse_touch_target.RA_second));
			sprintf_s(show_dec, " Dec: %d d %d m %d s", int(mouse_touch_target.DEC_degree), int(fabs(mouse_touch_target.DEC_min)), int(fabs(mouse_touch_target.DEC_second)));
			sprintf_s(show_ele, " Ele: %d d %d m %d.%d s", int(elevation_2_deg_min_sec[0]), abs(int(elevation_2_deg_min_sec[1])), abs(int(elevation_2_deg_min_sec[2])), int(100.0 * (elevation_2_deg_min_sec[2] - floor(elevation_2_deg_min_sec[2]))));
			sprintf_s(show_azi, " Azi: %d d %d m %d.%d s", int(azimuth_2_deg_min_sec[0]), int(azimuth_2_deg_min_sec[1]), int(azimuth_2_deg_min_sec[2]), int(100.0 * (azimuth_2_deg_min_sec[2] - floor(azimuth_2_deg_min_sec[2]))));

			if (mouse_touch_target.elevation < 0 || (noTarget && !mouse_touch_target.isStar)) {
				if (mouse_touch_target.elevation < 0) {
					cv::putText(star_image, "this target is below the horizon!", cv::Point(mouse_touch_target.position_in_image_x - double(heigth) / 20.0, mouse_touch_target.position_in_image_y - double(heigth) / 25.0), \
						cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
					cv::circle(star_image, cv::Point(mouse_touch_target.position_in_image_x, mouse_touch_target.position_in_image_y), double(heigth) / 50.0, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
				}
				else if (noTarget && !mouse_touch_target.isStar) {
					cv::putText(star_image, "please select a star!", cv::Point(mouse_touch_target.position_in_image_x - double(heigth) / 20.0, mouse_touch_target.position_in_image_y - double(heigth) / 25.0), \
						cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
					cv::circle(star_image, cv::Point(mouse_touch_target.position_in_image_x, mouse_touch_target.position_in_image_y), double(heigth) / 50.0, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
				}
			}
			else {
				cv::putText(star_image, "choosable star", cv::Point(mouse_touch_target.position_in_image_x - double(heigth) / 20.0, mouse_touch_target.position_in_image_y - double(heigth) / 25.0), \
					cv::FONT_HERSHEY_SIMPLEX, 0.5, optional_target_color, 1, cv::LINE_AA);
				cv::circle(star_image, cv::Point(mouse_touch_target.position_in_image_x, mouse_touch_target.position_in_image_y), double(heigth) / 50.0, optional_target_color, 1, cv::LINE_AA);
			}
			if (!mouse_touch_target.isStar) {
				cv::putText(star_image, show_target_name + mouse_touch_target.name, cv::Point(mouse_touch_target.position_in_image_x + double(heigth) / 50.0 + 3, mouse_touch_target.position_in_image_y + double(heigth) / 50.0 + 10), \
					cv::FONT_HERSHEY_SIMPLEX, 0.4, coordinate_words, 1, cv::LINE_AA);
			}
			else {
				cv::putText(star_image, show_target_name + mouse_touch_target.ShowName, cv::Point(mouse_touch_target.position_in_image_x + double(heigth) / 50.0 + 3, mouse_touch_target.position_in_image_y + double(heigth) / 50.0 + 10), \
					cv::FONT_HERSHEY_SIMPLEX, 0.4, coordinate_words, 1, cv::LINE_AA);
			}
			cv::putText(star_image, show_ra, cv::Point(mouse_touch_target.position_in_image_x + double(heigth) / 50.0 + 3, mouse_touch_target.position_in_image_y + 20 + double(heigth) / 50.0 + 10), \
				cv::FONT_HERSHEY_SIMPLEX, 0.4, coordinate_words, 1, cv::LINE_AA);
			cv::putText(star_image, show_dec, cv::Point(mouse_touch_target.position_in_image_x + double(heigth) / 50.0 + 3, mouse_touch_target.position_in_image_y + 40 + double(heigth) / 50.0 + 10), \
				cv::FONT_HERSHEY_SIMPLEX, 0.4, coordinate_words, 1, cv::LINE_AA);
			cv::putText(star_image, show_ele, cv::Point(mouse_touch_target.position_in_image_x + double(heigth) / 50.0 + 3, mouse_touch_target.position_in_image_y + 60 + double(heigth) / 50.0 + 10), \
				cv::FONT_HERSHEY_SIMPLEX, 0.4, coordinate_words, 1, cv::LINE_AA);
			cv::putText(star_image, show_azi, cv::Point(mouse_touch_target.position_in_image_x + double(heigth) / 50.0 + 3, mouse_touch_target.position_in_image_y + 80 + double(heigth) / 50.0 + 10), \
				cv::FONT_HERSHEY_SIMPLEX, 0.4, coordinate_words, 1, cv::LINE_AA);
		}
		/*-------------------------------------*/
		cv::imshow(win_name, star_image);
		cv::waitKey(50);
		if (mouse_get_coondinate) {//鼠标按下显示判断语句
			/*---------------对生成的图像进行初始化的代码段-----------------*/
			star_image = this->targetTable.getTargetTableImage();
			if (this->zero2star) {
				cv::putText(star_image, "Select a star with the mouse to calibrate the '0' position.", cv::Point(0 + double(heigth) / 200.0, 0 + double(heigth) / 50.0), \
					cv::FONT_HERSHEY_SIMPLEX, 0.6, show_state_1_color, 1, cv::LINE_AA);
			}
			else {
				cv::putText(star_image, "Select a observed object with the mouse.", cv::Point(0 + double(heigth) / 200.0, 0 + double(heigth) / 50.0), \
					cv::FONT_HERSHEY_SIMPLEX, 0.6, show_state_1_color, 1, cv::LINE_AA);
			}
			this->targetTable.get_image_x_y(this->start.RA_angle, this->start.DEC_angle, &this->start.position_in_image_x, &this->start.position_in_image_y);
			lsx::starProcessing::DrawRegularTriangle(star_image, this->start.position_in_image_x, this->start.position_in_image_y, my_equatorial_instrument_color, double(heigth) / 100.0);
			cv::circle(star_image, cv::Point(this->start.position_in_image_x, this->start.position_in_image_y), double(heigth) / 80.0, my_equatorial_instrument_color, 1, cv::LINE_AA);
			cv::putText(star_image, _EQ, cv::Point(this->start.position_in_image_x + double(heigth) / 60.0, this->start.position_in_image_y - double(heigth) / 60.0), \
				cv::FONT_HERSHEY_SIMPLEX, 0.4, my_equatorial_instrument_color, 1, cv::LINE_AA);
			/*---------------------*/
			deal_mouse_event = false;//没有处理,则回调函数不接受鼠标事件
			if (!goto_origin_position) {//按下左键
				choose_target = false;
				this->targetTable.findCloseTarget(mouse_botton_x, mouse_botton_y, find_radius, &choose_target);
				if (choose_target) {
					this->end = this->targetTable.findCloseTarget(mouse_botton_x, mouse_botton_y, find_radius, &choose_target);
					lsx::astronomy::Equ2EleAzi(&this->end);
					if (this->end.elevation < 0 || (noTarget && !this->end.isStar)) {
						if (this->end.elevation < 0) {
							cv::putText(star_image, "this target is below the horizon!", cv::Point(this->end.position_in_image_x - double(heigth) / 20.0, this->end.position_in_image_y - double(heigth) / 25.0), \
								cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
							cv::circle(star_image, cv::Point(this->end.position_in_image_x, this->end.position_in_image_y), double(heigth) / 50.0, cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
						}
						else if (noTarget && !this->end.isStar) {
							cv::putText(star_image, "please select a star!", cv::Point(this->end.position_in_image_x - double(heigth) / 20.0, this->end.position_in_image_y - double(heigth) / 25.0), \
								cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
							cv::circle(star_image, cv::Point(this->end.position_in_image_x, this->end.position_in_image_y), double(heigth) / 50.0, cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
						}
						mouse_get_coondinate = false;
						cv::imshow(win_name, star_image);
						cv::waitKey(60);
					}
					else {
						cv::putText(star_image, "selected successfully!", cv::Point(this->end.position_in_image_x - double(heigth) / 20.0, this->end.position_in_image_y - double(heigth) / 25.0), \
							cv::FONT_HERSHEY_SIMPLEX, 0.5, optional_target_color, 1, cv::LINE_AA);
						cv::circle(star_image, cv::Point(this->end.position_in_image_x, this->end.position_in_image_y), double(heigth) / 50.0, optional_target_color, 2, cv::LINE_AA);
						mouse_get_coondinate = false;
						cv::imshow(win_name, star_image);
						noTarget = false; after_guide = false;
						get_end_target = true;
						cv::waitKey(200);
					}
				}
				else {
					mouse_get_coondinate = false;
				}
			}
			else {//按下右键
				if (noTarget == false) {
					this->targetTable.get_ra_dec(mouse_botton_x, mouse_botton_y, &this->end.RA_angle, &this->end.DEC_angle);
					lsx::astronomy::Equ2EleAzi(&this->end);
					if (this->end.elevation < 0) {
						this->end.RA_angle = this->ra_in_region_0_360(local_sidreal_time_degree() + 90.0);
						this->end.DEC_angle = 90.0;
						this->end.DEC_degree = 90.0;
						this->end.name = "零位";
						this->end.number = "0";
						lsx::astronomy::degree2ra(this->end.RA_angle, &this->end.RA_hour, &this->end.RA_min, &this->end.RA_second);
						get_end_target = true;
						back_origin_position = true;
					}
					else {
						mouse_get_coondinate = false;
					}
				}
				else {
					mouse_get_coondinate = false;
					goto_origin_position = false;
				}
			}
			deal_mouse_event = true;
		}
		this->targetTable.findCloseTarget(mouse_touch_x, mouse_touch_y, find_radius, &touch_target);
		if (touch_target) {
			mouse_touch_target = this->targetTable.findCloseTarget(mouse_touch_x, mouse_touch_y, find_radius, &touch_target);
		}
		else {
			mouse_touch_target.position_in_image_x = -1;
			mouse_touch_target.position_in_image_y = -1;
		}
	}
	this_target_name = this->end.name;
	this_target_number = this->end.number;
	this->pointTarget = this->end;
	if (this->start.name != this->end.name) {
		/*----------------------------------------------------判断语句---------------------------------------------------------*/
		if (this->passZenith(this->start) && this->passZenith(this->end)) {//左左
			//std::cout << "LL" << std::endl;
			if (DEC_side == DEC_IS_ON_THE_RIGHT) {//异常情况处理
				DEC_side = DEC_IS_ON_THE_LEFT;
				ra_angle = 180.0 + this->ra_minAngle(lsx::astronomy::GetZenith(), this->start) - this->ra_minAngle(lsx::astronomy::GetZenith(), this->end);
				dec_angle = 180.0 - this->start.DEC_angle - this->end.DEC_angle;
				ra_direction = RA_DIR_BACKWARD;
				dec_angle = DEC_DIR_UP_WHEN_RIGHT;
			}
			else {
				ra_angle = this->ra_minAngle(lsx::astronomy::GetZenith(), this->start) - this->ra_minAngle(lsx::astronomy::GetZenith(), this->end);
				dec_angle = this->end.DEC_angle - this->start.DEC_angle;
				if (ra_angle > 0) {
					ra_direction = RA_DIR_BACKWARD;
				}
				else {
					ra_direction = RA_DIR_FORWARD;
				}
				if (dec_angle > 0) {
					dec_direction = DEC_DIR_UP_WHEN_LEFT;
				}
				else {
					dec_direction = DEC_DIR_DOWN_WHEN_LEFT;
				}
			}
		}
		else if (this->passZenith(this->start) && !this->passZenith(this->end)) {//左右
			//std::cout << "LR" << std::endl;
			if (DEC_side == DEC_IS_ON_THE_RIGHT) {//异常情况处理
				ra_angle = this->ra_minAngle(lsx::astronomy::GetZenith(), this->start) + this->ra_minAngle(lsx::astronomy::GetZenith(), this->end);
				dec_angle = this->end.DEC_angle - this->start.DEC_angle;
				ra_direction = RA_DIR_BACKWARD;
				if (dec_angle > 0) {
					dec_direction = DEC_DIR_UP_WHEN_RIGHT;
				}
				else {
					dec_direction = DEC_DIR_DOWN_WHEN_RIGHT;
				}
			}
			else {
				DEC_side = DEC_IS_ON_THE_RIGHT;
				ra_angle = 180.0 - this->ra_minAngle(lsx::astronomy::GetZenith(), this->end) - this->ra_minAngle(lsx::astronomy::GetZenith(), this->start);
				dec_angle = 180.0 - this->end.DEC_angle - this->start.DEC_angle;
				dec_direction = DEC_DIR_UP_WHEN_LEFT;
				if (ra_angle > 0) {
					ra_direction = RA_DIR_FORWARD;
				}
				else {
					ra_direction = RA_DIR_BACKWARD;
				}
			}
		}
		else if (!this->passZenith(this->start) && this->passZenith(this->end)) {//右左
			//std::cout << "RL" << std::endl;
			if (DEC_side == DEC_IS_ON_THE_LEFT) {
				ra_angle = this->ra_minAngle(lsx::astronomy::GetZenith(), this->start) + this->ra_minAngle(lsx::astronomy::GetZenith(), this->end);
				dec_angle = this->end.DEC_angle - this->start.DEC_angle;
				ra_direction = RA_DIR_FORWARD;
				if (dec_angle > 0) {
					dec_direction = DEC_DIR_UP_WHEN_LEFT;
				}
				else {
					dec_direction = DEC_DIR_DOWN_WHEN_LEFT;
				}
			}
			else {
				DEC_side = DEC_IS_ON_THE_LEFT;
				ra_angle = 180.0 - this->ra_minAngle(lsx::astronomy::GetZenith(), this->end) - this->ra_minAngle(lsx::astronomy::GetZenith(), this->start);
				dec_angle = 180.0 - this->end.DEC_angle - this->start.DEC_angle;
				dec_direction = DEC_DIR_UP_WHEN_RIGHT;
				if (ra_angle > 0) {
					ra_direction = RA_DIR_BACKWARD;
				}
				else {
					ra_direction = RA_DIR_FORWARD;
				}
			}
		}
		else if (!this->passZenith(this->start) && !this->passZenith(this->end)) {//右右
			//std::cout << "RR" << std::endl;
			if (DEC_side == DEC_IS_ON_THE_LEFT) {
				DEC_side = DEC_IS_ON_THE_RIGHT;
				ra_angle = 180.0 - this->ra_minAngle(lsx::astronomy::GetZenith(), this->end) + this->ra_minAngle(lsx::astronomy::GetZenith(), this->start);
				dec_angle = 180.0 - this->end.DEC_angle - this->start.DEC_angle;
				ra_direction = RA_DIR_FORWARD;
				dec_direction = DEC_DIR_UP_WHEN_LEFT;
			}
			else {
				ra_angle = this->ra_minAngle(lsx::astronomy::GetZenith(), this->end) - this->ra_minAngle(lsx::astronomy::GetZenith(), this->start);
				dec_angle = this->end.DEC_angle - this->start.DEC_angle;
				if (ra_angle > 0) {
					ra_direction = RA_DIR_BACKWARD;
				}
				else {
					ra_direction = RA_DIR_FORWARD;
				}
				if (dec_angle > 0) {
					dec_direction = DEC_DIR_UP_WHEN_RIGHT;
				}
				else {
					dec_direction = DEC_DIR_DOWN_WHEN_RIGHT;
				}
			}
		}
		/*----------------------------------------------------角度修正与信号发出---------------------------------------------------------*/
		if (fabs(dec_angle) > 254.0) {//角度超过串口发送极限
			dec_angle_part = fabs(dec_angle) - 254.0;
			dec_angle = 254.0;
			lsx::control::dec_goto_motion(dec_direction, dec_angle_part);
			Sleep(lsx::control::EquatorialRunTime_ms(dec_angle_part, DEC_STEPMOTOR, RA_WAIT_MODE_NORMAL) + 500);
		}
		lsx::control::dec_goto_motion(dec_direction, dec_angle);
		dec_runtime_ms = lsx::control::EquatorialRunTime_ms(dec_angle, DEC_STEPMOTOR, RA_WAIT_MODE_NORMAL);

		Sleep(2);

		if (!back_origin_position) {//考虑赤道仪的速度和地球速度,补偿赤道仪运行角度
			ra_angle = lsx::control::get_ra_goto_correct_angle(ra_angle, ra_direction);
		}
		lsx::control::ra_goto_motion(ra_direction, ra_angle);
		ra_runtime_ms = lsx::control::EquatorialRunTime_ms(ra_angle, RA_STEPMOTOR, RA_WAIT_MODE_NORMAL);

		/*---------------------------------------------------图像显示------------------------------------------------------*/

		this->targetTable.get_image_x_y(this->start.RA_angle, this->start.DEC_angle, &this->start.position_in_image_x, &this->start.position_in_image_y);
		this->targetTable.get_image_x_y(this->end.RA_angle, this->end.DEC_angle, &this->end.position_in_image_x, &this->end.position_in_image_y);
		start_time = std::clock();
		this->ra_arrival = false; this->dec_arrival = false;
		while (!arrive_the_target) {
			star_image = this->targetTable.getTargetTableImage();
			end_time = std::clock();
			now_run_time = double(end_time) - double(start_time);
			if (now_run_time > lsx::math::Max(ra_runtime_ms, dec_runtime_ms)) {
				cv::circle(star_image, cv::Point(this->end.position_in_image_x, this->end.position_in_image_y), double(heigth) / 50.0, optional_target_color, 2, cv::LINE_AA);
				lsx::starProcessing::DrawRegularTriangle(star_image, now_position.x, now_position.y, my_equatorial_instrument_color, double(heigth) / 100.0);
				cv::circle(star_image, now_position, double(heigth) / 80.0, my_equatorial_instrument_color, 1, cv::LINE_AA);
				cv::putText(star_image, "EQ arrival!", cv::Point(0 + double(heigth) / 200.0, 0 + double(heigth) / 50.0), \
					cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 255, 0), 1, cv::LINE_AA);
				cv::imshow(win_name, star_image);
				cv::waitKey(700);
				arrive_the_target = true;
				break;
			}
			if (now_run_time < ra_runtime_ms && now_run_time < dec_runtime_ms) {
				sprintf_s(show_my_equ, "my EQ(RA:%d ms, DEC:%d ms)", int(ra_runtime_ms - now_run_time), int(dec_runtime_ms - now_run_time));
			}
			else if (now_run_time >= ra_runtime_ms && now_run_time < dec_runtime_ms) {
				sprintf_s(show_my_equ, "my EQ(RA arrive, DEC:%d ms)", int(dec_runtime_ms - now_run_time));
			}
			else if (now_run_time < ra_runtime_ms && now_run_time >= dec_runtime_ms) {
				sprintf_s(show_my_equ, "my EQ(RA:%d ms, DEC arrive)", int(ra_runtime_ms - now_run_time));
			}
			else {
				sprintf_s(show_my_equ, "my EQ(RA arrive, DEC arrive)");
			}
			this->get_image_position(this->start.position_in_image_x, this->start.position_in_image_y, \
				this->end.position_in_image_x, this->end.position_in_image_y, ra_runtime_ms, dec_runtime_ms, start_time, end_time, &now_position);
			cv::circle(star_image, cv::Point(this->end.position_in_image_x, this->end.position_in_image_y), double(heigth) / 50.0, optional_target_color, 2, cv::LINE_AA);
			if (!back_origin_position) {
				cv::putText(star_image, "EQ on the move...", cv::Point(0 + double(heigth) / 200.0, 0 + double(heigth) / 50.0), \
					cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
			}
			else {
				cv::putText(star_image, "EQ on the move(return to '0' position)...", cv::Point(0 + double(heigth) / 200.0, 0 + double(heigth) / 50.0), \
					cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);
			}
			lsx::starProcessing::DrawRegularTriangle(star_image, now_position.x, now_position.y, my_equatorial_instrument_color, double(heigth) / 100.0);
			cv::circle(star_image, now_position, double(heigth) / 80.0, my_equatorial_instrument_color, 1, cv::LINE_AA);
			//cv::line(star_image, cv::Point(now_position.x + double(heigth) / 80.0, now_position.y), cv::Point(star_image.cols, now_position.y), my_equatorial_instrument_color, 1, cv::LINE_AA);//右侧线
			//cv::line(star_image, cv::Point(now_position.x - double(heigth) / 80.0, now_position.y), cv::Point(0, now_position.y), my_equatorial_instrument_color, 1, cv::LINE_AA);//左侧线
			//cv::line(star_image, cv::Point(now_position.x, now_position.y - double(heigth) / 80.0), cv::Point(now_position.x, 0), my_equatorial_instrument_color, 1, cv::LINE_AA);//上侧线
			//cv::line(star_image, cv::Point(now_position.x, now_position.y + double(heigth) / 80.0), cv::Point(now_position.x, star_image.rows), my_equatorial_instrument_color, 1, cv::LINE_AA);//下侧线
			cv::line(star_image, cv::Point(now_position.x + double(heigth) / 80.0, now_position.y), cv::Point(now_position.x + 6.0 * double(heigth) / 80.0, now_position.y), my_equatorial_instrument_line_color, 1, cv::LINE_AA);//右侧线
			cv::line(star_image, cv::Point(now_position.x - double(heigth) / 80.0, now_position.y), cv::Point(now_position.x - 6.0 * double(heigth) / 80.0, now_position.y), my_equatorial_instrument_line_color, 1, cv::LINE_AA);//左侧线
			cv::line(star_image, cv::Point(now_position.x, now_position.y - double(heigth) / 80.0), cv::Point(now_position.x, now_position.y - 6.0 * double(heigth) / 80.0), my_equatorial_instrument_line_color, 1, cv::LINE_AA);//上侧线
			cv::line(star_image, cv::Point(now_position.x, now_position.y + double(heigth) / 80.0), cv::Point(now_position.x, now_position.y + 6.0 * double(heigth) / 80.0), my_equatorial_instrument_line_color, 1, cv::LINE_AA);//下侧线
			cv::putText(star_image, show_my_equ, cv::Point(now_position.x + double(heigth) / 60.0, now_position.y - double(heigth) / 60.0), \
				cv::FONT_HERSHEY_SIMPLEX, 0.4, my_equatorial_instrument_color, 1, cv::LINE_AA);
			cv::imshow(win_name, star_image);
			cv::waitKey(20);
		}
		cv::destroyAllWindows();
		system("cls");
		if (this->end.isStar && !this->zero2star) {
			std::cout << std::endl;
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
			std::cout << "请将 <" << this->pointTarget.name << "> 放置于视场中心。" << std::endl;
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
			std::cout << std::endl;
			lsx::control::kbControl(CONTROL_RA_DEC);
		}
		if (this->zero2star) {
			this->zero2star = false;
			std::cout << std::endl;
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
			std::cout << "为了校准零位,请将 <" << this->pointTarget.name << "> 放置于视场中心。" << std::endl;
			SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
			std::cout << std::endl;
			lsx::control::kbControl(CONTROL_RA_DEC);
		}
	}
	else {
		cv::destroyAllWindows();
		system("cls");
	}
}

void lsx::autoSearch::AutoStarFindController::preSet(std::string targetName, std::string targetNumber, int decSide) {
	this_target_name = targetName;
	this_target_number = targetNumber;
	DEC_side = decSide;
	noTarget = false;
}

void lsx::autoSearch::AutoStarFindController::AutomaticStarFinding() {
	this->gotoControl();
}

主函数中自动寻星的调用:

void test_goto() {
	lsx::autoSearch::AutoStarFindController myAutoStarFinding;
	while (true) {
		myAutoStarFinding.AutomaticStarFinding();
	}
}

运行后的图形用户界面:

可以通过鼠标选择天体:

当赤道仪在零位时必须选择一个恒星,之后可以选择其他的天体,当然地平线以下的也不能选择:

选择正确的天体后软件就会给赤道仪下发指令开始移动,移动方式请参考论文。

<think>嗯,用户想了解Excel中的VLOOKUP函数的用法和参数含义。首先,我需要回忆一下VLOOKUP的基本结构。VLOOKUP是垂直查找函数,用于在表格的首列查找指定的值,然后返回该行中指定列的数据。它的四个参数分别是查找值、表格范围、列索引号和匹配模式。 接下来,我得确认每个参数的具体作用。第一个参数是lookup_value,也就是用户要查找的值,比如产品ID或者姓名。第二个参数是table_array,即查找的范围,这里要注意的是,查找值必须位于这个范围的第一列,否则函数无法正确工作。第三个参数是col_index_num,也就是用户想要返回的数据在表格范围中的第几列,这个列数是从表格范围的第一列开始算起的,不是整个工作表的列。第四个参数是range_lookup,决定是精确匹配还是近似匹配,通常用FALSE或0来指定精确匹配,避免错误。 然后,用户可能需要一个具体的例子来理解如何使用。比如,假设有一个产品价格表,用户想根据产品ID查找对应的价格。这时候,VLOOKUP就可以在ID列查找,然后返回价格列的数据。同时,要提醒用户注意表格范围最好使用绝对引用,比如$A$2:$C$100,这样在拖动公式时范围不会变化。 可能遇到的问题也需要考虑。比如,如果查找值不在表格的第一列,或者列索引号超过了表格的列数,函数会返回错误。此外,数据格式不一致也可能导致问题,比如查找值是数字,而表格中是文本格式的数字,这时候需要统一格式。还有,当出现#N/A错误时,可能是查找值不存在,或者匹配模式设置不正确,需要检查参数是否正确。 用户可能还会问到如何避免常见的错误,比如部分数据匹配出错的情况。这时候需要检查是否有多余的空格,或者数据格式不一致,或者表格范围没有锁定导致拖动时偏移。此外,XLOOKUP作为VLOOKUP的升级版,可能在某些情况下更方便,比如可以向左查找或者返回多列数据,这也是可以提及的内容。 最后,整理步骤,从基础语法到参数说明,再到具体案例,逐步引导用户理解如何使用VLOOKUP函数。同时,提供常见错误和解决方法,帮助用户在实际应用中避免问题。如果有必要,还可以比较VLOOKUP和其他查找函数的区别,比如HLOOKUP或XLOOKUP,但根据用户的问题,主要集中在VLOOKUP本身即可。</think>### Excel VLOOKUP函数详解 #### 一、基础语法与参数说明 VLOOKUP函数的语法为: $$=VLOOKUP(lookup\_value, table\_array, col\_index\_num, [range\_lookup])$$ 包含4个参数: 1. **lookup_value**(必填):要查找的值(如单元格引用或具体值) 2. **table_array**(必填):包含数据的表格范围(必须包含查找列和返回列) 3. **col_index_num**(必填):返回值所在列的序号(从table_array第一列开始计数) 4. **range_lookup**(可选):匹配类型 - `TRUE`/`1`:近似匹配(默认值,需数据升序排列) - `FALSE`/`0`:精确匹配(常用选项) [^1][^2] #### 二、使用步骤演示(工资表查询案例) 假设需要根据员工编号查询工资: 1. 建立查询单元格(如`B12`) 2. 输入公式: ```excel =VLOOKUP(A12, $A$2:$D$100, 4, 0) ``` - `A12`:待查询的员工编号 - `$A$2:$D$100`:锁定数据区域(绝对引用) - `4`:返回第4列(工资列) - `0`:精确匹配 [^2][^3] #### 三、常见错误与解决方法 | 错误现象 | 原因 | 解决方案 | |---------|------|---------| | #N/A | 查找值不存在 | 检查数据源或改用`IFERROR`容错 | | #REF! | 列序号超出范围 | 确认col_index_num ≤ 表格列数 | | 部分匹配失败 | 数据格式不一致 | 统一数值/文本格式 | | 结果错位 | 表格未锁定 | 使用`$`符号固定区域引用 | [^3][^4] #### 四、进阶技巧 1. **多条件查询**: 使用辅助列合并多个条件字段 ```excel =VLOOKUP(A2&B2, $D$2:$F$100, 3, 0) ``` 2. **通配符匹配**: `"*"`匹配任意字符,`"?"`匹配单个字符 ```excel =VLOOKUP("张*", $A$2:$C$100, 3, 0) ``` 3. **跨表查询**: 引用其他工作表数据 ```excel =VLOOKUP(A2, Sheet2!$A$2:$D$100, 4, 0) ``` [^1][^4]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伪程序猿l S x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值