From aaae84a77cc7dfd94c293b19e887b96a8d290068 Mon Sep 17 00:00:00 2001 From: Lesser324 <383538326@qq.com> Date: Wed, 16 Jul 2025 10:10:30 +0800 Subject: [PATCH] =?UTF-8?q?qtcreator=E7=89=88=E6=9C=AC=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E6=94=AF=E6=8C=81=E5=9C=A8qtcreator=E4=B8=AD=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E9=A1=B5=E9=9D=A2=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qtCreatorVer/dialogs/noise_dialog.py | 57 +++++ qtCreatorVer/dialogs/overscan_dialog.py | 56 ++++ qtCreatorVer/dialogs/ptc_dialog.py | 77 ++++++ qtCreatorVer/main.py | 35 +++ qtCreatorVer/pyui/mainwindow_ui.py | 133 ++++++++++ qtCreatorVer/pyui/noise_dialog_ui.py | 91 +++++++ qtCreatorVer/pyui/overscan_dialog_ui.py | 91 +++++++ qtCreatorVer/pyui/ptc_dialog_ui.py | 91 +++++++ qtCreatorVer/ui/generate_pyui.sh | 13 + qtCreatorVer/ui/mainwindow.ui | 185 ++++++++++++++ qtCreatorVer/ui/noise_dialog.ui | 82 ++++++ qtCreatorVer/ui/overscan_dialog.ui | 82 ++++++ qtCreatorVer/ui/ptc_dialog.ui | 82 ++++++ qtCreatorVer/util/getNoise.py | 84 ++++++ qtCreatorVer/util/plot_prc.py | 68 +++++ qtCreatorVer/util/processPTC.py | 327 ++++++++++++++++++++++++ qtCreatorVer/util/remove_overscan.py | 55 ++++ qtCreatorVer/workers/process_worker.py | 30 +++ 18 files changed, 1639 insertions(+) create mode 100644 qtCreatorVer/dialogs/noise_dialog.py create mode 100644 qtCreatorVer/dialogs/overscan_dialog.py create mode 100644 qtCreatorVer/dialogs/ptc_dialog.py create mode 100644 qtCreatorVer/main.py create mode 100644 qtCreatorVer/pyui/mainwindow_ui.py create mode 100644 qtCreatorVer/pyui/noise_dialog_ui.py create mode 100644 qtCreatorVer/pyui/overscan_dialog_ui.py create mode 100644 qtCreatorVer/pyui/ptc_dialog_ui.py create mode 100644 qtCreatorVer/ui/generate_pyui.sh create mode 100644 qtCreatorVer/ui/mainwindow.ui create mode 100644 qtCreatorVer/ui/noise_dialog.ui create mode 100644 qtCreatorVer/ui/overscan_dialog.ui create mode 100644 qtCreatorVer/ui/ptc_dialog.ui create mode 100644 qtCreatorVer/util/getNoise.py create mode 100755 qtCreatorVer/util/plot_prc.py create mode 100755 qtCreatorVer/util/processPTC.py create mode 100644 qtCreatorVer/util/remove_overscan.py create mode 100644 qtCreatorVer/workers/process_worker.py diff --git a/qtCreatorVer/dialogs/noise_dialog.py b/qtCreatorVer/dialogs/noise_dialog.py new file mode 100644 index 0000000..c174a3d --- /dev/null +++ b/qtCreatorVer/dialogs/noise_dialog.py @@ -0,0 +1,57 @@ +from PySide6.QtWidgets import QDialog, QFileDialog, QMessageBox +from pyui.noise_dialog_ui import Ui_NoiseDialog +import sys +import os +import datetime +from workers.process_worker import ProcessWorker + +class NoiseDialog(QDialog, Ui_NoiseDialog): + def __init__(self, parent=None, log_widget=None): + super().__init__(parent) + self.setupUi(self) + self.log_widget = log_widget + self.inBrowse.clicked.connect(self.browse_input) + self.outBrowse.clicked.connect(self.browse_output) + self.startBtn.clicked.connect(self.run_noise) + self.worker = None + + def browse_input(self): + file, _ = QFileDialog.getOpenFileName(self, "选择FITS文件", "", "FITS Files (*.fits)") + if file: + self.inEdit.setText(file) + + def browse_output(self): + directory = QFileDialog.getExistingDirectory(self, "选择输出目录") + if directory: + self.outEdit.setText(directory) + + def run_noise(self): + fits_file = self.inEdit.text().strip() + out_dir = self.outEdit.text().strip() + if not fits_file or not os.path.isfile(fits_file): + QMessageBox.warning(self, "参数错误", "请选择有效的输入FITS文件!") + return + if not out_dir: + out_dir = os.path.dirname(fits_file) # 默认用输入文件所在目录 + if self.log_widget: + self.log_widget.append(f"开始进行Bias/Noise处理") + cmd = [sys.executable, os.path.abspath("util/getNoise.py"), fits_file] + self.startBtn.setEnabled(False) + self.worker = ProcessWorker(cmd, cwd=out_dir) + self.worker.output_signal.connect(self.append_log) + self.worker.finished_signal.connect(lambda code: self.process_finished(code, out_dir)) + self.worker.start() + self.accept() # 立即关闭对话框 + + def append_log(self, text): + if self.log_widget: + self.log_widget.append(text) + else: + QMessageBox.information(self, "运行结果", text) + + def process_finished(self, code, result_dir): + self.startBtn.setEnabled(True) + if code == 0: + self.append_log(f"Bias/Noise处理完成,结果保存在: {result_dir}") + else: + self.append_log("Bias/Noise处理失败或中断。") \ No newline at end of file diff --git a/qtCreatorVer/dialogs/overscan_dialog.py b/qtCreatorVer/dialogs/overscan_dialog.py new file mode 100644 index 0000000..770f4b9 --- /dev/null +++ b/qtCreatorVer/dialogs/overscan_dialog.py @@ -0,0 +1,56 @@ +from PySide6.QtWidgets import QDialog, QFileDialog, QMessageBox +from pyui.overscan_dialog_ui import Ui_OverscanDialog +import sys +import os +from workers.process_worker import ProcessWorker + +class OverscanDialog(QDialog, Ui_OverscanDialog): + def __init__(self, parent=None, log_widget=None): + super().__init__(parent) + self.setupUi(self) + self.log_widget = log_widget + self.inBrowse.clicked.connect(self.browse_input) + self.outBrowse.clicked.connect(self.browse_output) + self.startBtn.clicked.connect(self.run_overscan) + self.worker = None + + def browse_input(self): + file, _ = QFileDialog.getOpenFileName(self, "选择FITS文件", "", "FITS Files (*.fits)") + if file: + self.inEdit.setText(file) + + def browse_output(self): + directory = QFileDialog.getExistingDirectory(self, "选择输出目录") + if directory: + self.outEdit.setText(directory) + + def run_overscan(self): + fits_file = self.inEdit.text().strip() + out_dir = self.outEdit.text().strip() + if not fits_file or not os.path.isfile(fits_file): + QMessageBox.warning(self, "参数错误", "请选择有效的输入FITS文件!") + return + if not out_dir: + out_dir = os.path.dirname(fits_file) # 默认用输入文件所在目录 + if self.log_widget: + self.log_widget.append(f"开始进行过扫区修正处理") + cmd = [sys.executable, os.path.abspath("util/remove_overscan.py"), fits_file] + self.startBtn.setEnabled(False) + self.worker = ProcessWorker(cmd, cwd=out_dir) + self.worker.output_signal.connect(self.append_log) + self.worker.finished_signal.connect(lambda code: self.process_finished(code, out_dir)) + self.worker.start() + self.accept() # 立即关闭对话框 + + def append_log(self, text): + if self.log_widget: + self.log_widget.append(text) + else: + QMessageBox.information(self, "运行结果", text) + + def process_finished(self, code, out_dir): + self.startBtn.setEnabled(True) + if code == 0: + self.append_log(f"过扫区修正处理完成,结果保存在: {out_dir}") + else: + self.append_log("过扫区修正处理失败或中断。") \ No newline at end of file diff --git a/qtCreatorVer/dialogs/ptc_dialog.py b/qtCreatorVer/dialogs/ptc_dialog.py new file mode 100644 index 0000000..5fc0276 --- /dev/null +++ b/qtCreatorVer/dialogs/ptc_dialog.py @@ -0,0 +1,77 @@ +from PySide6.QtWidgets import QDialog, QFileDialog, QMessageBox +from pyui.ptc_dialog_ui import Ui_PTCDialog +import sys +import os +import datetime +from workers.process_worker import ProcessWorker + +class PTCDialog(QDialog, Ui_PTCDialog): + def __init__(self, parent=None, log_widget=None): + super().__init__(parent) + self.setupUi(self) + self.log_widget = log_widget + self.inBrowse.clicked.connect(self.browse_input) + self.outBrowse.clicked.connect(self.browse_output) + self.startBtn.clicked.connect(self.run_ptc) + self.worker = None + self.prc_worker = None # 新增PRC分析工作线程 + + def browse_input(self): + directory = QFileDialog.getExistingDirectory(self, "选择输入目录") + if directory: + self.inEdit.setText(directory) + + def browse_output(self): + directory = QFileDialog.getExistingDirectory(self, "选择输出目录") + if directory: + self.outEdit.setText(directory) + + def run_ptc(self): + in_dir = self.inEdit.text().strip() + out_dir = self.outEdit.text().strip() + if not in_dir: + QMessageBox.warning(self, "参数错误", "请选择输入目录!") + return + if not out_dir: + out_dir = in_dir # 如果未选择输出目录,默认用输入目录 + # 新建带时间戳的子文件夹 + timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') + result_dir = os.path.join(out_dir, f'PTC_PRC_result_{timestamp}') + try: + os.makedirs(result_dir, exist_ok=True) + except Exception as e: + QMessageBox.critical(self, "文件夹创建失败", f"无法创建结果文件夹: {result_dir}\n错误: {e}") + return + self.append_log(f"开始进行PTC处理") + cmd = [sys.executable, os.path.abspath("util/processPTC.py"), in_dir, result_dir] + self.startBtn.setEnabled(False) + self.worker = ProcessWorker(cmd) + self.worker.output_signal.connect(self.append_log) + self.worker.finished_signal.connect(lambda code: self.process_finished(code, result_dir)) + self.worker.start() + self.accept() # 立即关闭对话框 + + def append_log(self, text): + if self.log_widget: + self.log_widget.append(text) + else: + QMessageBox.information(self, "运行结果", text) + + def process_finished(self, code, result_dir): + self.startBtn.setEnabled(True) + if code == 0: + self.append_log(f"PTC处理完成,正在进行PRC分析...\n") + # 自动调用plot_prc.py + prc_cmd = [sys.executable, os.path.abspath("util/plot_prc.py"), result_dir] + self.prc_worker = ProcessWorker(prc_cmd) + self.prc_worker.output_signal.connect(self.append_log) + self.prc_worker.finished_signal.connect(lambda code: self.prc_finished(code, result_dir)) + self.prc_worker.start() + else: + self.append_log("PTC处理失败或中断。") + + def prc_finished(self, code, result_dir): + if code == 0: + self.append_log("PRC分析完成!\n结果保存在: {}".format(result_dir)) + else: + self.append_log("PRC分析失败或中断。") \ No newline at end of file diff --git a/qtCreatorVer/main.py b/qtCreatorVer/main.py new file mode 100644 index 0000000..9064f59 --- /dev/null +++ b/qtCreatorVer/main.py @@ -0,0 +1,35 @@ +import subprocess +from PySide6.QtWidgets import QApplication, QMainWindow +from pyui.mainwindow_ui import Ui_MainWindow +from dialogs.noise_dialog import NoiseDialog +from dialogs.ptc_dialog import PTCDialog +from dialogs.overscan_dialog import OverscanDialog +import sys +import os +import datetime + +class MainWindow(QMainWindow, Ui_MainWindow): + def __init__(self): + super().__init__() + self.setupUi(self) + self.noiseButton.clicked.connect(self.show_noise_dialog) + self.ptcButton.clicked.connect(self.show_ptc_dialog) + self.overscanButton.clicked.connect(self.show_overscan_dialog) + + def show_noise_dialog(self): + dialog = NoiseDialog(self, log_widget=self.logTextEdit) + dialog.exec() + + def show_ptc_dialog(self): + dialog = PTCDialog(self, log_widget=self.logTextEdit) + dialog.exec() + + def show_overscan_dialog(self): + dialog = OverscanDialog(self, log_widget=self.logTextEdit) + dialog.exec() + +if __name__ == "__main__": + app = QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec()) diff --git a/qtCreatorVer/pyui/mainwindow_ui.py b/qtCreatorVer/pyui/mainwindow_ui.py new file mode 100644 index 0000000..27ab343 --- /dev/null +++ b/qtCreatorVer/pyui/mainwindow_ui.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'mainwindow.ui' +## +## Created by: Qt User Interface Compiler version 5.15.16 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import * # type: ignore +from PySide6.QtGui import * # type: ignore +from PySide6.QtWidgets import * # type: ignore + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + if not MainWindow.objectName(): + MainWindow.setObjectName(u"MainWindow") + MainWindow.resize(900, 500) + self.centralwidget = QWidget(MainWindow) + self.centralwidget.setObjectName(u"centralwidget") + self.mainHLayout = QHBoxLayout(self.centralwidget) + self.mainHLayout.setObjectName(u"mainHLayout") + self.mainVLayout = QVBoxLayout() + self.mainVLayout.setObjectName(u"mainVLayout") + self.titleLabel = QLabel(self.centralwidget) + self.titleLabel.setObjectName(u"titleLabel") + self.titleLabel.setMinimumSize(QSize(398, 25)) + self.titleLabel.setMaximumSize(QSize(1000, 25)) + font = QFont() + font.setPointSize(18) + font.setBold(True) + self.titleLabel.setFont(font) + self.titleLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) + + self.mainVLayout.addWidget(self.titleLabel) + + self.radioLayout = QHBoxLayout() + self.radioLayout.setObjectName(u"radioLayout") + self.radioLayout.setContentsMargins(12, -1, -1, -1) + self.radioLabel = QLabel(self.centralwidget) + self.radioLabel.setObjectName(u"radioLabel") + self.radioLabel.setMaximumSize(QSize(200, 30)) + + self.radioLayout.addWidget(self.radioLabel) + + self.radio290_00 = QRadioButton(self.centralwidget) + self.radio290_00.setObjectName(u"radio290_00") + self.radio290_00.setMaximumSize(QSize(200, 30)) + self.radio290_00.setChecked(True) + + self.radioLayout.addWidget(self.radio290_00) + + self.radio47_20 = QRadioButton(self.centralwidget) + self.radio47_20.setObjectName(u"radio47_20") + self.radio47_20.setMaximumSize(QSize(200, 30)) + + self.radioLayout.addWidget(self.radio47_20) + + + self.mainVLayout.addLayout(self.radioLayout) + + self.buttonsLayout1 = QHBoxLayout() + self.buttonsLayout1.setObjectName(u"buttonsLayout1") + self.ptcButton = QPushButton(self.centralwidget) + self.ptcButton.setObjectName(u"ptcButton") + self.ptcButton.setMaximumSize(QSize(200, 30)) + + self.buttonsLayout1.addWidget(self.ptcButton) + + self.noiseButton = QPushButton(self.centralwidget) + self.noiseButton.setObjectName(u"noiseButton") + self.noiseButton.setMaximumSize(QSize(200, 30)) + + self.buttonsLayout1.addWidget(self.noiseButton) + + + self.mainVLayout.addLayout(self.buttonsLayout1) + + self.buttonsLayout2 = QHBoxLayout() + self.buttonsLayout2.setObjectName(u"buttonsLayout2") + self.linearityButton = QPushButton(self.centralwidget) + self.linearityButton.setObjectName(u"linearityButton") + self.linearityButton.setMaximumSize(QSize(200, 30)) + + self.buttonsLayout2.addWidget(self.linearityButton) + + self.overscanButton = QPushButton(self.centralwidget) + self.overscanButton.setObjectName(u"overscanButton") + self.overscanButton.setMaximumSize(QSize(200, 30)) + + self.buttonsLayout2.addWidget(self.overscanButton) + + + self.mainVLayout.addLayout(self.buttonsLayout2) + + + self.mainHLayout.addLayout(self.mainVLayout) + + self.logTextEdit = QTextEdit(self.centralwidget) + self.logTextEdit.setObjectName(u"logTextEdit") + self.logTextEdit.setReadOnly(True) + + self.mainHLayout.addWidget(self.logTextEdit) + + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QMenuBar(MainWindow) + self.menubar.setObjectName(u"menubar") + self.menubar.setGeometry(QRect(0, 0, 900, 24)) + MainWindow.setMenuBar(self.menubar) + self.statusbar = QStatusBar(MainWindow) + self.statusbar.setObjectName(u"statusbar") + MainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(MainWindow) + + QMetaObject.connectSlotsByName(MainWindow) + # setupUi + + def retranslateUi(self, MainWindow): + MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"CCD\u6570\u636e\u5904\u7406\u5de5\u5177", None)) + self.titleLabel.setText(QCoreApplication.translate("MainWindow", u"CCD\u6570\u636e\u5904\u7406\u5de5\u5177", None)) + self.radioLabel.setText(QCoreApplication.translate("MainWindow", u"\u9009\u62e9CCD\u7c7b\u578b:", None)) + self.radio290_00.setText(QCoreApplication.translate("MainWindow", u"290-99", None)) + self.radio47_20.setText(QCoreApplication.translate("MainWindow", u"47-20", None)) + self.ptcButton.setText(QCoreApplication.translate("MainWindow", u"PTC/PRC", None)) + self.noiseButton.setText(QCoreApplication.translate("MainWindow", u"Bias/Noise", None)) + self.linearityButton.setText(QCoreApplication.translate("MainWindow", u"Linearity", None)) + self.overscanButton.setText(QCoreApplication.translate("MainWindow", u"\u8fc7\u626b\u533a\u4fee\u6b63", None)) + self.logTextEdit.setPlaceholderText(QCoreApplication.translate("MainWindow", u"\u65e5\u5fd7\u8f93\u51fa...", None)) + # retranslateUi + diff --git a/qtCreatorVer/pyui/noise_dialog_ui.py b/qtCreatorVer/pyui/noise_dialog_ui.py new file mode 100644 index 0000000..3562b84 --- /dev/null +++ b/qtCreatorVer/pyui/noise_dialog_ui.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'noise_dialog.ui' +## +## Created by: Qt User Interface Compiler version 5.15.16 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import * # type: ignore +from PySide6.QtGui import * # type: ignore +from PySide6.QtWidgets import * # type: ignore + + +class Ui_NoiseDialog(object): + def setupUi(self, NoiseDialog): + if not NoiseDialog.objectName(): + NoiseDialog.setObjectName(u"NoiseDialog") + NoiseDialog.setMinimumWidth(400) + self.verticalLayout = QVBoxLayout(NoiseDialog) + self.verticalLayout.setObjectName(u"verticalLayout") + self.inLayout = QHBoxLayout() + self.inLayout.setObjectName(u"inLayout") + self.inLabel = QLabel(NoiseDialog) + self.inLabel.setObjectName(u"inLabel") + + self.inLayout.addWidget(self.inLabel) + + self.inEdit = QLineEdit(NoiseDialog) + self.inEdit.setObjectName(u"inEdit") + + self.inLayout.addWidget(self.inEdit) + + self.inBrowse = QPushButton(NoiseDialog) + self.inBrowse.setObjectName(u"inBrowse") + + self.inLayout.addWidget(self.inBrowse) + + + self.verticalLayout.addLayout(self.inLayout) + + self.outLayout = QHBoxLayout() + self.outLayout.setObjectName(u"outLayout") + self.outLabel = QLabel(NoiseDialog) + self.outLabel.setObjectName(u"outLabel") + + self.outLayout.addWidget(self.outLabel) + + self.outEdit = QLineEdit(NoiseDialog) + self.outEdit.setObjectName(u"outEdit") + + self.outLayout.addWidget(self.outEdit) + + self.outBrowse = QPushButton(NoiseDialog) + self.outBrowse.setObjectName(u"outBrowse") + + self.outLayout.addWidget(self.outBrowse) + + + self.verticalLayout.addLayout(self.outLayout) + + self.btnLayout = QHBoxLayout() + self.btnLayout.setObjectName(u"btnLayout") + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.btnLayout.addItem(self.horizontalSpacer) + + self.startBtn = QPushButton(NoiseDialog) + self.startBtn.setObjectName(u"startBtn") + + self.btnLayout.addWidget(self.startBtn) + + + self.verticalLayout.addLayout(self.btnLayout) + + + self.retranslateUi(NoiseDialog) + + QMetaObject.connectSlotsByName(NoiseDialog) + # setupUi + + def retranslateUi(self, NoiseDialog): + NoiseDialog.setWindowTitle(QCoreApplication.translate("NoiseDialog", u"Bias/Noise\u53c2\u6570\u8bbe\u7f6e", None)) + self.inLabel.setText(QCoreApplication.translate("NoiseDialog", u"\u8f93\u5165FITS\u6587\u4ef6:", None)) + self.inBrowse.setText(QCoreApplication.translate("NoiseDialog", u"\u6d4f\u89c8", None)) + self.outLabel.setText(QCoreApplication.translate("NoiseDialog", u"\u8f93\u51fa\u76ee\u5f55:", None)) + self.outBrowse.setText(QCoreApplication.translate("NoiseDialog", u"\u6d4f\u89c8", None)) + self.startBtn.setText(QCoreApplication.translate("NoiseDialog", u"\u5f00\u59cb\u5904\u7406", None)) + # retranslateUi + diff --git a/qtCreatorVer/pyui/overscan_dialog_ui.py b/qtCreatorVer/pyui/overscan_dialog_ui.py new file mode 100644 index 0000000..5fc70e4 --- /dev/null +++ b/qtCreatorVer/pyui/overscan_dialog_ui.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'overscan_dialog.ui' +## +## Created by: Qt User Interface Compiler version 5.15.16 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import * # type: ignore +from PySide6.QtGui import * # type: ignore +from PySide6.QtWidgets import * # type: ignore + + +class Ui_OverscanDialog(object): + def setupUi(self, OverscanDialog): + if not OverscanDialog.objectName(): + OverscanDialog.setObjectName(u"OverscanDialog") + OverscanDialog.setMinimumWidth(400) + self.verticalLayout = QVBoxLayout(OverscanDialog) + self.verticalLayout.setObjectName(u"verticalLayout") + self.inLayout = QHBoxLayout() + self.inLayout.setObjectName(u"inLayout") + self.inLabel = QLabel(OverscanDialog) + self.inLabel.setObjectName(u"inLabel") + + self.inLayout.addWidget(self.inLabel) + + self.inEdit = QLineEdit(OverscanDialog) + self.inEdit.setObjectName(u"inEdit") + + self.inLayout.addWidget(self.inEdit) + + self.inBrowse = QPushButton(OverscanDialog) + self.inBrowse.setObjectName(u"inBrowse") + + self.inLayout.addWidget(self.inBrowse) + + + self.verticalLayout.addLayout(self.inLayout) + + self.outLayout = QHBoxLayout() + self.outLayout.setObjectName(u"outLayout") + self.outLabel = QLabel(OverscanDialog) + self.outLabel.setObjectName(u"outLabel") + + self.outLayout.addWidget(self.outLabel) + + self.outEdit = QLineEdit(OverscanDialog) + self.outEdit.setObjectName(u"outEdit") + + self.outLayout.addWidget(self.outEdit) + + self.outBrowse = QPushButton(OverscanDialog) + self.outBrowse.setObjectName(u"outBrowse") + + self.outLayout.addWidget(self.outBrowse) + + + self.verticalLayout.addLayout(self.outLayout) + + self.btnLayout = QHBoxLayout() + self.btnLayout.setObjectName(u"btnLayout") + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.btnLayout.addItem(self.horizontalSpacer) + + self.startBtn = QPushButton(OverscanDialog) + self.startBtn.setObjectName(u"startBtn") + + self.btnLayout.addWidget(self.startBtn) + + + self.verticalLayout.addLayout(self.btnLayout) + + + self.retranslateUi(OverscanDialog) + + QMetaObject.connectSlotsByName(OverscanDialog) + # setupUi + + def retranslateUi(self, OverscanDialog): + OverscanDialog.setWindowTitle(QCoreApplication.translate("OverscanDialog", u"\u8fc7\u626b\u533a\u4fee\u6b63\u53c2\u6570\u8bbe\u7f6e", None)) + self.inLabel.setText(QCoreApplication.translate("OverscanDialog", u"\u8f93\u5165FITS\u6587\u4ef6:", None)) + self.inBrowse.setText(QCoreApplication.translate("OverscanDialog", u"\u6d4f\u89c8", None)) + self.outLabel.setText(QCoreApplication.translate("OverscanDialog", u"\u8f93\u51fa\u76ee\u5f55:", None)) + self.outBrowse.setText(QCoreApplication.translate("OverscanDialog", u"\u6d4f\u89c8", None)) + self.startBtn.setText(QCoreApplication.translate("OverscanDialog", u"\u5f00\u59cb\u5904\u7406", None)) + # retranslateUi + diff --git a/qtCreatorVer/pyui/ptc_dialog_ui.py b/qtCreatorVer/pyui/ptc_dialog_ui.py new file mode 100644 index 0000000..7a61c02 --- /dev/null +++ b/qtCreatorVer/pyui/ptc_dialog_ui.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'ptc_dialog.ui' +## +## Created by: Qt User Interface Compiler version 5.15.16 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import * # type: ignore +from PySide6.QtGui import * # type: ignore +from PySide6.QtWidgets import * # type: ignore + + +class Ui_PTCDialog(object): + def setupUi(self, PTCDialog): + if not PTCDialog.objectName(): + PTCDialog.setObjectName(u"PTCDialog") + PTCDialog.setMinimumWidth(400) + self.verticalLayout = QVBoxLayout(PTCDialog) + self.verticalLayout.setObjectName(u"verticalLayout") + self.inLayout = QHBoxLayout() + self.inLayout.setObjectName(u"inLayout") + self.inLabel = QLabel(PTCDialog) + self.inLabel.setObjectName(u"inLabel") + + self.inLayout.addWidget(self.inLabel) + + self.inEdit = QLineEdit(PTCDialog) + self.inEdit.setObjectName(u"inEdit") + + self.inLayout.addWidget(self.inEdit) + + self.inBrowse = QPushButton(PTCDialog) + self.inBrowse.setObjectName(u"inBrowse") + + self.inLayout.addWidget(self.inBrowse) + + + self.verticalLayout.addLayout(self.inLayout) + + self.outLayout = QHBoxLayout() + self.outLayout.setObjectName(u"outLayout") + self.outLabel = QLabel(PTCDialog) + self.outLabel.setObjectName(u"outLabel") + + self.outLayout.addWidget(self.outLabel) + + self.outEdit = QLineEdit(PTCDialog) + self.outEdit.setObjectName(u"outEdit") + + self.outLayout.addWidget(self.outEdit) + + self.outBrowse = QPushButton(PTCDialog) + self.outBrowse.setObjectName(u"outBrowse") + + self.outLayout.addWidget(self.outBrowse) + + + self.verticalLayout.addLayout(self.outLayout) + + self.btnLayout = QHBoxLayout() + self.btnLayout.setObjectName(u"btnLayout") + self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum) + + self.btnLayout.addItem(self.horizontalSpacer) + + self.startBtn = QPushButton(PTCDialog) + self.startBtn.setObjectName(u"startBtn") + + self.btnLayout.addWidget(self.startBtn) + + + self.verticalLayout.addLayout(self.btnLayout) + + + self.retranslateUi(PTCDialog) + + QMetaObject.connectSlotsByName(PTCDialog) + # setupUi + + def retranslateUi(self, PTCDialog): + PTCDialog.setWindowTitle(QCoreApplication.translate("PTCDialog", u"PTC/PRC\u53c2\u6570\u8bbe\u7f6e", None)) + self.inLabel.setText(QCoreApplication.translate("PTCDialog", u"\u8f93\u5165\u76ee\u5f55:", None)) + self.inBrowse.setText(QCoreApplication.translate("PTCDialog", u"\u6d4f\u89c8", None)) + self.outLabel.setText(QCoreApplication.translate("PTCDialog", u"\u8f93\u51fa\u76ee\u5f55:", None)) + self.outBrowse.setText(QCoreApplication.translate("PTCDialog", u"\u6d4f\u89c8", None)) + self.startBtn.setText(QCoreApplication.translate("PTCDialog", u"\u5f00\u59cb\u5904\u7406", None)) + # retranslateUi + diff --git a/qtCreatorVer/ui/generate_pyui.sh b/qtCreatorVer/ui/generate_pyui.sh new file mode 100644 index 0000000..34464e8 --- /dev/null +++ b/qtCreatorVer/ui/generate_pyui.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# 自动将ui目录下所有.ui文件编译为pyui目录下的 _ui.py 文件 +# 需要已安装 pyside6-uic + +OUTDIR="../pyui" +mkdir -p "$OUTDIR" + +for ui_file in *.ui; do + base_name=$(basename "$ui_file" .ui) + pyside6-uic "$ui_file" -o "$OUTDIR/${base_name}_ui.py" +done + +echo "所有UI文件已编译到 $OUTDIR 下。" \ No newline at end of file diff --git a/qtCreatorVer/ui/mainwindow.ui b/qtCreatorVer/ui/mainwindow.ui new file mode 100644 index 0000000..f539ca3 --- /dev/null +++ b/qtCreatorVer/ui/mainwindow.ui @@ -0,0 +1,185 @@ + + + MainWindow + + + + 0 + 0 + 900 + 500 + + + + CCD数据处理工具 + + + + + + + + + + 398 + 25 + + + + + 1000 + 25 + + + + + 18 + true + + + + CCD数据处理工具 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + 12 + + + + + + 200 + 30 + + + + 选择CCD类型: + + + + + + + + 200 + 30 + + + + 290-99 + + + true + + + + + + + + 200 + 30 + + + + 47-20 + + + + + + + + + + + + 200 + 30 + + + + PTC/PRC + + + + + + + + 200 + 30 + + + + Bias/Noise + + + + + + + + + + + + 200 + 30 + + + + Linearity + + + + + + + + 200 + 30 + + + + 过扫区修正 + + + + + + + + + + + true + + + 日志输出... + + + + + + + + + 0 + 0 + 900 + 24 + + + + + + + + diff --git a/qtCreatorVer/ui/noise_dialog.ui b/qtCreatorVer/ui/noise_dialog.ui new file mode 100644 index 0000000..b35cb3f --- /dev/null +++ b/qtCreatorVer/ui/noise_dialog.ui @@ -0,0 +1,82 @@ + + + NoiseDialog + + + Bias/Noise参数设置 + + + 400 + + + + + + + + 输入FITS文件: + + + + + + + + + + 浏览 + + + + + + + + + + + 输出目录: + + + + + + + + + + 浏览 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 开始处理 + + + + + + + + + + \ No newline at end of file diff --git a/qtCreatorVer/ui/overscan_dialog.ui b/qtCreatorVer/ui/overscan_dialog.ui new file mode 100644 index 0000000..d71dbbb --- /dev/null +++ b/qtCreatorVer/ui/overscan_dialog.ui @@ -0,0 +1,82 @@ + + + OverscanDialog + + + 过扫区修正参数设置 + + + 400 + + + + + + + + 输入FITS文件: + + + + + + + + + + 浏览 + + + + + + + + + + + 输出目录: + + + + + + + + + + 浏览 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 开始处理 + + + + + + + + + + \ No newline at end of file diff --git a/qtCreatorVer/ui/ptc_dialog.ui b/qtCreatorVer/ui/ptc_dialog.ui new file mode 100644 index 0000000..be1ed66 --- /dev/null +++ b/qtCreatorVer/ui/ptc_dialog.ui @@ -0,0 +1,82 @@ + + + PTCDialog + + + PTC/PRC参数设置 + + + 400 + + + + + + + + 输入目录: + + + + + + + + + + 浏览 + + + + + + + + + + + 输出目录: + + + + + + + + + + 浏览 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 开始处理 + + + + + + + + + + \ No newline at end of file diff --git a/qtCreatorVer/util/getNoise.py b/qtCreatorVer/util/getNoise.py new file mode 100644 index 0000000..b5c896d --- /dev/null +++ b/qtCreatorVer/util/getNoise.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +import os, sys +import time +from astropy.io import fits +import numpy as np +from scipy.stats import sigmaclip +from astropy.table import Table + +def write_log(log_msg): + + curTime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + msg = f'[{curTime}] -- run getNoise.py and saved {log_msg}\n' + if os.path.exists('noise_log.txt'): + fp = open('noise_log.txt', 'a') + else: + fp = open('noise_log.txt', 'w') + + fp.write(msg) + fp.close() + +def remove_overscan(imgdata): + print('> doing overscan correction ...') + img = np.zeros((9232,9216), dtype=float) + + for i in range(4): + img[0:4616, i*1152:(i+1)*1152] = imgdata[0:4616,(i*1250+27):((i+1)*1250-71)] + img[4616:, i*1152:(i+1)*1152] = imgdata[4784:,(i*1250+27):((i+1)*1250-71)] + oc1 = np.mean(imgdata[0:4616,((i+1)*1250-71):((i+1)*1250)], axis=1).astype(float) + oc2 = np.mean(imgdata[4784:,((i+1)*1250-71):((i+1)*1250)], axis=1).astype(float) +# print('oc1.shape: {}'.format(oc1.shape)) + for j in range(4616): + img[j,i*1152:(i+1)*1152] = img[j,i*1152:(i+1)*1152] - oc1[j] + img[4616+j,i*1152:(i+1)*1152] = img[4616+j,i*1152:(i+1)*1152]- oc2[j] + for i in range(4,8): + img[0:4616, i*1152:(i+1)*1152] = imgdata[0:4616,(i*1250+71):((i+1)*1250-27)] + img[4616:, i*1152:(i+1)*1152] = imgdata[4784:,(i*1250+71):((i+1)*1250-27)] + oc1 = np.mean(imgdata[0:4616,(i*1250):(i*1250+71)], axis=1).astype(float) + oc2 = np.mean(imgdata[4784:,(i*1250):(i*1250+71)], axis=1).astype(float) + for j in range(4616): + img[j, i*1152:(i+1)*1152] = img[j, i*1152:(i+1)*1152] - oc1[j] + img[4616+j,i*1152:(i+1)*1152] = img[4616+j,i*1152:(i+1)*1152] - oc2[j] + + print('> overscan correction done!') + return img + +def main(): + fname = sys.argv[1] + imgdata = fits.getdata(fname).astype(float) + img = remove_overscan(imgdata) + + os = [] + noise_clip_4_4 = [] + noise_clip_3_3 = [] + for ch in range(16): +# print('> estimating rms for ch-{:02d}'.format(ch+1)) + os.append(ch+1) + if ch < 8: + ch_data = img[0:4616,ch*1152:(ch+1)*1152] + else: + ch_data = img[4616:, (15-ch)*1152:(16-ch)*1152] + + rms44 = np.std( sigmaclip(ch_data, 4, 4)[0] ) + rms33 = np.std( sigmaclip(ch_data, 3, 3)[0] ) + noise_clip_4_4.append( np.round(rms44,4) ) + noise_clip_3_3.append( np.round(rms33,4) ) + + tab = Table() + tab['OS'] = os + tab['Noise_Clip_4_4'] = noise_clip_4_4 + tab['Noise_Clip_3_3'] = noise_clip_3_3 + print(tab) + curTime = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime()) + + tab_name = fname.replace('.fits','') + '_{}.tab'.format(curTime) + tab.write(tab_name, format='ipac', overwrite=True) + write_log(tab_name) + +# print('\n# Channel noise(clip,4,4) noise(clip,3,3)\n') +# for i in range(16): +# print('> OS-{:02d} {:6.3f} {:6.3f}'.format(i, noise_clip_4_4[i], noise_clip_3_3[i])) + +if __name__ == '__main__': + main() diff --git a/qtCreatorVer/util/plot_prc.py b/qtCreatorVer/util/plot_prc.py new file mode 100755 index 0000000..9a2bb49 --- /dev/null +++ b/qtCreatorVer/util/plot_prc.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +import os, sys +import numpy as np +from astropy.table import Table +import matplotlib.pylab as plt +from scipy import odr + +def plotPRC(out_dir): + tab = Table.read(f'{out_dir}/ptcdata.tab', format='ipac') + + model = odr.polynomial(1) + + fig = plt.figure(figsize=(20,40)) + + for ch in range(16): + row = ch // 4 + pos1 = row*8 + ch - row*4 + 1 + pos2 = pos1 + 4 + + # print(f'pos1: {pos1}, pos2: {pos2}') + + plt.subplot(8,4,pos1) + + index = tab['channel'] == ch+1 + + t_exp = tab[index]['t_exp'] + flux = tab[index]['flux_4'] + + # fit prc and calculate residuals + data = odr.Data(t_exp, flux, we=1/(t_exp**2), wd=1/(flux**2) ) + # data = odr.Data(t_exp, flux, we=1/(t_exp**2) ) + # data = odr.Data(t_exp, flux) + fitter = odr.ODR(data, model, ifixb=[1,1], beta0=[2500,800]) + res = fitter.run() + poly = np.poly1d(res.beta[::-1]) + nonlin = (flux-poly(t_exp)) / poly(t_exp) * 100 + + plt.scatter(t_exp, flux/1e3, zorder=1, alpha=0.85, color='m', edgecolors='none', s=30) + # plt.plot(t_exp, flux/1e3, 'g--o', alpha=0.75) + plt.plot(t_exp, poly(t_exp)/1e3, 'b--', alpha=0.75) + plt.xlabel('Expourse Time (s)') + plt.ylabel('Flux (kDN)') + plt.ylim([-1,6.1e1]) + # plt.title('channel {}'.format(ch+1)) + plt.text(5,55,'channel {}'.format(ch+1), color='limegreen', fontsize=14) + plt.grid() + + + plt.subplot(8,4,pos2) + print(res.beta) + plt.scatter(t_exp, nonlin, alpha=0.75, color='darkorange', edgecolors='none', s=30) + plt.hlines(y=1.8, xmin=t_exp.min(), xmax=t_exp.max(), linestyle=':', color='dimgrey') + plt.hlines(y=-1.8, xmin=t_exp.min(), xmax=t_exp.max(), linestyle=':', color='dimgrey') + plt.plot(t_exp, np.zeros(len(t_exp)), '--', color='grey') + plt.ylim([-3.25,3.25]) + plt.xlabel('Expourse Time (s)') + plt.ylabel('PRC Nonlinearity (%)') + # plt.title('channel {}'.format(ch+1)) + plt.text(5,2.5,'channel {}'.format(ch+1), color='brown', fontsize=14) + plt.grid() + + plt.tight_layout() + plt.savefig(f'{out_dir}/prc.pdf') + plt.close() + +if __name__ == '__main__': + plotPRC(sys.argv[1]) diff --git a/qtCreatorVer/util/processPTC.py b/qtCreatorVer/util/processPTC.py new file mode 100755 index 0000000..983b25b --- /dev/null +++ b/qtCreatorVer/util/processPTC.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python + +import glob, os, sys +from tqdm import trange +from astropy.table import Table +from astropy.io import fits +from scipy.stats import sigmaclip +from scipy import odr +import numpy as np +from matplotlib import pyplot as plt + +def remove_overscan(imgdata, ch): + # print('> removing 71 cols overscan ...') + img = np.zeros((4616,1152), dtype=float) + +# for i in range(4): +# img[0:4616, i*1152:(i+1)*1152] = imgdata[0:4616,(i*1250+27):((i+1)*1250-71)] +# img[4616:, i*1152:(i+1)*1152] = imgdata[4784:,(i*1250+27):((i+1)*1250-71)] +# oc1 = np.median(imgdata[0:4616,((i+1)*1250-71):((i+1)*1250)], axis=1).astype(float) +# oc2 = np.median(imgdata[4784:,((i+1)*1250-71):((i+1)*1250)], axis=1).astype(float) +# # print('oc1.shape: {}'.format(oc1.shape)) +# for j in range(4616): +# img[j,i*1152:(i+1)*1152] = img[j,i*1152:(i+1)*1152] - oc1[j] +# img[4616+j,i*1152:(i+1)*1152] = img[4616+j,i*1152:(i+1)*1152]- oc2[j] +# for i in range(4,8): +# img[0:4616, i*1152:(i+1)*1152] = imgdata[0:4616,(i*1250+71):((i+1)*1250-27)] +# img[4616:, i*1152:(i+1)*1152] = imgdata[4784:,(i*1250+71):((i+1)*1250-27)] +# oc1 = np.median(imgdata[0:4616,(i*1250):(i*1250+71)], axis=1).astype(float) +# oc2 = np.median(imgdata[4784:,(i*1250):(i*1250+71)], axis=1).astype(float) +# for j in range(4616): +# img[j, i*1152:(i+1)*1152] = img[j, i*1152:(i+1)*1152] - oc1[j] +# img[4616+j,i*1152:(i+1)*1152] = img[4616+j,i*1152:(i+1)*1152] - oc2[j] + + if ch < 4: + img = imgdata[0:4616,(ch*1250+27):((ch+1)*1250-71)] + oc1 = np.median(imgdata[0:4616,((ch+1)*1250-71):((ch+1)*1250)], axis=1).astype(float) + for j in range(4616): + img[j,:] = img[j,:] - oc1[j] + elif ch < 8: + img = imgdata[0:4616,(ch*1250+71):((ch+1)*1250-27)] + oc1 = np.median(imgdata[0:4616,(ch*1250):(ch*1250+71)], axis=1).astype(float) + for j in range(4616): + img[j, :] = img[j,:] - oc1[j] + elif ch < 12: + img = imgdata[4784:,((15-ch)*1250+71):((16-ch)*1250-27)] + oc2 = np.median(imgdata[4784:,((15-ch)*1250):((15-ch)*1250+71)], axis=1).astype(float) + for j in range(4616): + img[j,:] = img[j,:] - oc2[j] + else: + img = imgdata[4784:,((15-ch)*1250+27):((16-ch)*1250-71)] + oc2 = np.median(imgdata[4784:,((16-ch)*1250-71):((16-ch)*1250)], axis=1).astype(float) + for j in range(4616): + img[j,:] = img[j,:]- oc2[j] + + return img + +def bindata(image, n): + ny, nx = image.shape + nx2 = nx // n + x_res = nx - nx2 * n + x1 = x_res // 2 + x2 = x_res - x1 + ny2 = ny // n + y_res = ny - ny2 * n + y1 = y_res // 2 + y2 = y_res - y1 + data = image[y1:ny-y2, x1:nx-x2] + # data = data.reshape((ny2, n, nx2, n)).sum(axis=1).sum(axis=-1) + data = data.reshape((ny2, n, nx2, n)).mean(axis=1).mean(axis=-1) + return data + +#blist = glob.glob("fits/bias/*.fits") + +#bias = None +#bcnt = 0 +#for i in range(len(blist)): +# if i==0: +# bias = fits.getdata(blist[i]).astype(float) +# else: +# bias += fits.getdata(blist[i]).astype(float) + +# bcnt += 1 + +#bias = bias / bcnt + + +def processPTC(fits_dir, out_dir): + + if os.path.exists(out_dir) is False: + os.system(f'mkdir {out_dir}') + + ftable = f'{out_dir}/flat_filelist.tab' + fdata = f'{out_dir}/ptcdata.tab' + + if os.path.exists(ftable) is False: + flist = glob.glob(f'{fits_dir}/*ptc*.fits') + t_exp = [] + seq = [] + for f in flist: + name = os.path.basename(f) + tmp = name.split('_') +# print(tmp) + t_exp.append(float(tmp[-4].replace('s', ''))) + seq.append(int(tmp[-2].replace('.fits', ''))) + tab = Table() + tab['name'] = flist + tab['t_exp'] = t_exp + tab['seq'] = seq + tab.sort(['t_exp', 'seq']) + tab.write(ftable, format='ipac', overwrite=True) + +# print('#########') +# sys.exit(0) + + clip_n = 4 + tab = Table.read(ftable, format='ipac') + if os.path.exists(fdata) is False: + t_exp = np.unique(tab['t_exp']) + t_exp = t_exp[t_exp < 35] + t = [] + chans = [] + flux_1 = [] + var_1 = [] + flux_2 = [] + var_2 = [] + flux_4 = [] + var_4 = [] + flux_8 = [] + var_8 = [] + for chan in range(16): + for i in trange(len(t_exp), desc='channel {}'.format(chan+1)): + index = tab['t_exp'] == t_exp[i] + f1, f2 = tab['name'][index][0], tab['name'][index][1] + with fits.open(f1) as img1, fits.open(f2) as img2: + t.append(t_exp[i]) + chans.append(chan+1) + # m1 = img1[0].data.astype(float)[:4617, chan*1250+72:chan*1250+1224] + # m2 = img2[0].data.astype(float)[:4617, chan*1250+72:chan*1250+1224] + + # if chan < 4: + # m1 = img1[0].data.astype(float)[2:4617, chan*1250+30:chan*1250+30+1170] - bias[2:4617, chan*1250+30:chan*1250+30+1170]*0 + # m2 = img2[0].data.astype(float)[2:4617, chan*1250+30:chan*1250+30+1170] - bias[2:4617, chan*1250+30:chan*1250+30+1170]*0 + # elif chan >= 4 and chan < 8: + # m1 = img1[0].data.astype(float)[2:4617, chan*1250+72:chan*1250+72+1170] - bias[2:4617, chan*1250+72:chan*1250+72+1170]*0 + # m2 = img2[0].data.astype(float)[2:4617, chan*1250+72:chan*1250+72+1170] - bias[2:4617, chan*1250+72:chan*1250+72+1170]*0 + # elif chan >= 8 and chan < 12: + # m1 = img1[0].data.astype(float)[4785:, (15-chan)*1250+72:(15-chan)*1250+72+1170] - bias[4785:, (15-chan)*1250+72:(15-chan)*1250+72+1170]*0 + # m2 = img2[0].data.astype(float)[4785:, (15-chan)*1250+72:(15-chan)*1250+72+1170] - bias[4785:, (15-chan)*1250+72:(15-chan)*1250+72+1170]*0 + # else: + # m1 = img1[0].data.astype(float)[4785:, (15-chan)*1250+30:(15-chan)*1250+30+1170] - bias[4785:, (15-chan)*1250+30:(15-chan)*1250+30+1170]*0 + # m2 = img2[0].data.astype(float)[4785:, (15-chan)*1250+30:(15-chan)*1250+30+1170] - bias[4785:, (15-chan)*1250+30:(15-chan)*1250+30+1170]*0 + +# if chan < 4: +# m1 = img1[0].data.astype(float)[2:4617, chan*1250+30:chan*1250+30+1170] #- bias[2:4617, chan*1250+30:chan*1250+30+1170]*0 +# m2 = img2[0].data.astype(float)[2:4617, chan*1250+30:chan*1250+30+1170] #- bias[2:4617, chan*1250+30:chan*1250+30+1170]*0 +# elif chan >= 4 and chan < 8: +# m1 = img1[0].data.astype(float)[2:4617, chan*1250+72:chan*1250+72+1170] #- bias[2:4617, chan*1250+72:chan*1250+72+1170]*0 +# m2 = img2[0].data.astype(float)[2:4617, chan*1250+72:chan*1250+72+1170] #- bias[2:4617, chan*1250+72:chan*1250+72+1170]*0 +# elif chan >= 8 and chan < 12: +# m1 = img1[0].data.astype(float)[4785:, (15-chan)*1250+72:(15-chan)*1250+72+1170] #- bias[4785:, (15-chan)*1250+72:(15-chan)*1250+72+1170]*0 +# m2 = img2[0].data.astype(float)[4785:, (15-chan)*1250+72:(15-chan)*1250+72+1170] #- bias[4785:, (15-chan)*1250+72:(15-chan)*1250+72+1170]*0 +# else: +# m1 = img1[0].data.astype(float)[4785:, (15-chan)*1250+30:(15-chan)*1250+30+1170] #- bias[4785:, (15-chan)*1250+30:(15-chan)*1250+30+1170]*0 +# m2 = img2[0].data.astype(float)[4785:, (15-chan)*1250+30:(15-chan)*1250+30+1170] #- bias[4785:, (15-chan)*1250+30:(15-chan)*1250+30+1170]*0 + + m1 = remove_overscan(img1[0].data, chan) + m2 = remove_overscan(img2[0].data, chan) + + flux_1.append(np.median(m1 + m2) / 2) + var_1.append(sigmaclip(m1 - m2, clip_n, clip_n)[0].var() / 2) + + mm1 = bindata(m1, 2) + mm2 = bindata(m2, 2) + flux_2.append(np.median(mm1 + mm2) / 2) + var_2.append(sigmaclip(mm1 - mm2, clip_n, clip_n)[0].var() / 2 * 4) + + mm1 = bindata(m1, 4) + mm2 = bindata(m2, 4) + flux_4.append(np.median(mm1 + mm2) / 2) + var_4.append(sigmaclip(mm1 - mm2, clip_n, clip_n)[0].var() / 2 * 16) + + mm1 = bindata(m1, 8) + mm2 = bindata(m2, 8) + flux_8.append(np.median(mm1 + mm2) / 2) + var_8.append(sigmaclip(mm1 - mm2, clip_n, clip_n)[0].var() / 2 * 64) + + cat = Table() + cat['t_exp'] = t + cat['channel'] = chans + cat['flux_1'] = flux_1 + cat['var_1'] = var_1 + cat['flux_2'] = flux_2 + cat['var_2'] = var_2 + cat['flux_4'] = flux_4 + cat['var_4'] = var_4 + cat['flux_8'] = flux_8 + cat['var_8'] = var_8 + cat.sort(['channel', 't_exp']) + cat.write(fdata, format='ipac', overwrite=True) + + + cat = Table.read(fdata, format='ipac') + for chan in np.unique(cat['channel']): + index = cat['channel'] == chan + ind = np.argmin(cat['t_exp'][index]) + cat['flux_1'][index] = cat['flux_1'][index] - cat['flux_1'][index][ind] + cat['var_1'][index] = cat['var_1'][index] - cat['var_1'][index][ind] + cat['flux_2'][index] = cat['flux_2'][index] - cat['flux_2'][index][ind] + cat['var_2'][index] = cat['var_2'][index] - cat['var_2'][index][ind] + cat['flux_4'][index] = cat['flux_4'][index] - cat['flux_4'][index][ind] + cat['var_4'][index] = cat['var_4'][index] - cat['var_4'][index][ind] + cat['flux_8'][index] = cat['flux_8'][index] - cat['flux_8'][index][ind] + cat['var_8'][index] = cat['var_8'][index] - cat['var_8'][index][ind] + index = cat['t_exp'] > 0 + cat = cat[index] + + model = odr.polynomial(1) + plt.figure(figsize=(20, 40)) + output_gains = [] + output_chans = [] + output_bins = [] + for chan in range(16): + index = cat['channel'] == chan+1 + bins = ['1', '2', '4', '8'] + colors = ['red', 'orange', 'blue', 'green'] + handlers = [] + txts = [] + row = chan // 4 + pos1 = row * 8 + chan - row * 4 + 1 + pos2 = pos1 + 4 + for i in range(4): + if i != 2: + pass + else: + x = cat['flux_'+bins[i]][index] + y = cat['var_'+bins[i]][index] + print('x = ', x) + print('y = ', y) + idx = x > 0 + x = x[idx] + y = y[idx] + fw = x[np.argmax(y)] + ind = (x > fw * 0.1) * (x < fw * 0.8) + data = odr.Data(x[ind], y[ind], we=1/(x[ind]**2), wd=1/(y[ind]**2)) + fitter = odr.ODR(data, model, ifixb=[0, 1], beta0=[0, 1]) + res = fitter.run() + poly = np.poly1d(res.beta[::-1]) + nonlin = (y - poly(x)) / poly(x) * 100 + + plt.subplot(8, 4, pos1) + plt.scatter(x[~ind], y[~ind], color=colors[i], zorder=1, alpha=0.25, edgecolors='none', s=20) + s = plt.scatter(x[ind], y[ind], color=colors[i], zorder=2, edgecolors='none', s=20) + l, = plt.plot(x, poly(x), color=colors[i], zorder=3, ls='--') + handlers.append(s) + txts.append('bin {}: gain = {:.3f} e-/DN'.format(bins[i], 1/res.beta[1])) + + output_gains.append(1/res.beta[1]) + output_chans.append(chan+1) + output_bins.append(bins[i]) + + plt.subplot(8, 4, pos2) + plt.scatter(x[~ind], nonlin[~ind], color=colors[i], zorder=2, alpha=0.25, edgecolors='none', s=20) + plt.scatter(x[ind], nonlin[ind], color=colors[i], zorder=3, edgecolors='none', s=20) + plt.plot(x, np.zeros(len(x)), color='grey', zorder=1) + + +# x = cat['flux_'+bins[i]][index] +# y = cat['var_'+bins[i]][index] +# print('x = ', x) +# print('y = ', y) +# idx = x > 0 +# x = x[idx] +# y = y[idx] +# fw = x[np.argmax(y)] +# ind = (x > fw * 0.1) * (x < fw * 0.8) +# data = odr.Data(x[ind], y[ind], we=1/(x[ind]**2), wd=1/(y[ind]**2)) +# fitter = odr.ODR(data, model, ifixb=[0, 1], beta0=[0, 1]) +# res = fitter.run() +# poly = np.poly1d(res.beta[::-1]) +# nonlin = (y - poly(x)) / poly(x) * 100 + +# plt.subplot(8, 4, pos1) +# plt.scatter(x[~ind], y[~ind], color=colors[i], zorder=1, alpha=0.25, edgecolors='none', s=20) +# s = plt.scatter(x[ind], y[ind], color=colors[i], zorder=2, edgecolors='none', s=20) +# l, = plt.plot(x, poly(x), color=colors[i], zorder=3, ls='--') +# handlers.append(s) +# txts.append('bin {}: gain = {:.3f} e-/DN'.format(bins[i], 1/res.beta[1])) + +# output_gains.append(1/res.beta[1]) +# output_chans.append(chan+1) +# output_bins.append(bins[i]) + +# plt.subplot(8, 4, pos2) +# plt.scatter(x[~ind], nonlin[~ind], color=colors[i], zorder=2, alpha=0.25, edgecolors='none', s=20) +# plt.scatter(x[ind], nonlin[ind], color=colors[i], zorder=3, edgecolors='none', s=20) +# plt.plot(x, np.zeros(len(x)), color='grey', zorder=1) + + plt.subplot(8, 4, pos1) + plt.xlabel('flux (DN)') + plt.ylabel('variance (DN^2)') + plt.title('channel {}'.format(chan+1)) + # plt.text(4e4,6e3,'channel {}'.format(chan+1), color='limegreen', fontsize=14) + plt.legend(handlers, txts) + plt.grid(True) + + plt.subplot(8, 4, pos2) + # plt.xlabel('flux (ADU)') + plt.xlabel('flux (DN)') + plt.ylabel('non-linearity (%)') + plt.title('channel {}'.format(chan+1)) + # plt.text(4e4,8,'channel {}'.format(chan+1), color='brown', fontsize=14) + plt.ylim([-10, 10]) + plt.grid(True) + + plt.tight_layout() + plt.savefig(f'{out_dir}/ptc.pdf') + plt.close() + tab = Table() + tab['channel'] = output_chans + tab['bin'] = output_bins + tab['gain'] = output_gains + tab.write(f'{out_dir}/gain.tab', format='ipac', overwrite=True) + + +if __name__ == '__main__': + processPTC(sys.argv[1], sys.argv[2]) + diff --git a/qtCreatorVer/util/remove_overscan.py b/qtCreatorVer/util/remove_overscan.py new file mode 100644 index 0000000..887220f --- /dev/null +++ b/qtCreatorVer/util/remove_overscan.py @@ -0,0 +1,55 @@ +import os, sys +import time +from astropy.io import fits +import numpy as np +from scipy.stats import sigmaclip +from astropy.table import Table + + +def remove_overscan(imgdata): + print('> doing overscan correction ...') + img = np.zeros((9232,9216), dtype=float) + + for i in range(4): + img[0:4616, i*1152:(i+1)*1152] = imgdata[0:4616,(i*1250+27):((i+1)*1250-71)] + img[4616:, i*1152:(i+1)*1152] = imgdata[4784:,(i*1250+27):((i+1)*1250-71)] + oc1 = np.mean(imgdata[0:4616,((i+1)*1250-71):((i+1)*1250)], axis=1).astype(float) + oc2 = np.mean(imgdata[4784:,((i+1)*1250-71):((i+1)*1250)], axis=1).astype(float) +# print('oc1.shape: {}'.format(oc1.shape)) + for j in range(4616): + img[j,i*1152:(i+1)*1152] = img[j,i*1152:(i+1)*1152] - oc1[j] + img[4616+j,i*1152:(i+1)*1152] = img[4616+j,i*1152:(i+1)*1152]- oc2[j] + for i in range(4,8): + img[0:4616, i*1152:(i+1)*1152] = imgdata[0:4616,(i*1250+71):((i+1)*1250-27)] + img[4616:, i*1152:(i+1)*1152] = imgdata[4784:,(i*1250+71):((i+1)*1250-27)] + oc1 = np.mean(imgdata[0:4616,(i*1250):(i*1250+71)], axis=1).astype(float) + oc2 = np.mean(imgdata[4784:,(i*1250):(i*1250+71)], axis=1).astype(float) + for j in range(4616): + img[j, i*1152:(i+1)*1152] = img[j, i*1152:(i+1)*1152] - oc1[j] + img[4616+j,i*1152:(i+1)*1152] = img[4616+j,i*1152:(i+1)*1152] - oc2[j] + + print('> overscan correction done!') + return img + +if __name__ == '__main__': + if len(sys.argv) < 2: + print('用法: python remove_overscan.py 输入FITS文件 [输出目录]') + sys.exit(1) + in_fits = sys.argv[1] + if not os.path.isfile(in_fits): + print(f'输入文件不存在: {in_fits}') + sys.exit(1) + if len(sys.argv) > 2: + out_dir = sys.argv[2] + else: + out_dir = os.path.dirname(in_fits) + os.makedirs(out_dir, exist_ok=True) + base = os.path.splitext(os.path.basename(in_fits))[0] + out_fits = os.path.join(out_dir, f'{base}_overscan_corrected.fits') + + with fits.open(in_fits) as hdul: + imgdata = hdul[0].data + header = hdul[0].header + img_corr = remove_overscan(imgdata) + fits.writeto(out_fits, img_corr, header, overwrite=True) + print(f'> 修正后文件已保存: {out_fits}') \ No newline at end of file diff --git a/qtCreatorVer/workers/process_worker.py b/qtCreatorVer/workers/process_worker.py new file mode 100644 index 0000000..4b38996 --- /dev/null +++ b/qtCreatorVer/workers/process_worker.py @@ -0,0 +1,30 @@ +import subprocess +from PySide6.QtCore import QThread, Signal + +class ProcessWorker(QThread): + output_signal = Signal(str) + finished_signal = Signal(int) + + def __init__(self, cmd, cwd=None): + super().__init__() + self.cmd = cmd + self.cwd = cwd + + def run(self): + try: + proc = subprocess.Popen( + self.cmd, + cwd=self.cwd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1 + ) + for line in proc.stdout: + self.output_signal.emit(line.rstrip()) + proc.stdout.close() + proc.wait() + self.finished_signal.emit(proc.returncode) + except Exception as e: + self.output_signal.emit(f'运行出错: {e}') + self.finished_signal.emit(-1) \ No newline at end of file -- Gitee