| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2023-11-19 06:43:20 +01:00
										 |  |  | // Copyright (C) 2021-2022 Jon Beniston, M7RCE <jon@beniston.com>                //
 | 
					
						
							|  |  |  | // Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com>               //
 | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is free software; you can redistribute it and/or modify          //
 | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by          //
 | 
					
						
							|  |  |  | // the Free Software Foundation as version 3 of the License, or                  //
 | 
					
						
							|  |  |  | // (at your option) any later version.                                           //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful,               //
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of                //
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
 | 
					
						
							|  |  |  | // GNU General Public License V3 for more details.                               //
 | 
					
						
							|  |  |  | //                                                                               //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU General Public License             //
 | 
					
						
							|  |  |  | // along with this program. If not, see <http://www.gnu.org/licenses/>.          //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2021-01-29 16:08:32 +00:00
										 |  |  | #include <cmath>
 | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-29 16:08:32 +00:00
										 |  |  | #include <QtGlobal>
 | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  | #include <QRegExp>
 | 
					
						
							|  |  |  | #include <QDebug>
 | 
					
						
							|  |  |  | #include <QResource>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "fits.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FITS::FITS(QString resourceName) : | 
					
						
							|  |  |  |     m_valid(false) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QResource m_res(resourceName); | 
					
						
							| 
									
										
										
										
											2021-10-12 11:07:56 +01:00
										 |  |  |     if (!m_res.isValid()) { | 
					
						
							|  |  |  |         qWarning() << "FITS: - " << resourceName << " is not a valid resource"; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  |     int m_headerSize = 2880; | 
					
						
							| 
									
										
										
										
											2021-01-29 16:08:32 +00:00
										 |  |  |     qint64 m_fileSize; | 
					
						
							|  |  |  | #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
 | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  |     m_data = m_res.uncompressedData(); | 
					
						
							| 
									
										
										
										
											2021-01-29 16:08:32 +00:00
										 |  |  |     m_fileSize = m_res.uncompressedSize(); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     m_data = QByteArray::fromRawData((const char *)m_res.data(), m_res.size()); | 
					
						
							| 
									
										
										
										
											2022-02-10 01:22:55 +01:00
										 |  |  |     if (m_res.isCompressed()) { | 
					
						
							| 
									
										
										
										
											2022-02-09 16:41:40 +00:00
										 |  |  |         m_data = qUncompress(m_data); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-01-29 16:08:32 +00:00
										 |  |  |     m_fileSize = m_res.size(); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     int hLen = std::min((qint64)m_headerSize * 3, m_fileSize);   // Could possibly be bigger
 | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  |     QByteArray headerBytes = m_data.left(hLen); | 
					
						
							|  |  |  |     QString header = QString::fromLatin1(headerBytes); | 
					
						
							|  |  |  |     QRegExp widthRE("NAXIS1 *= *([0-9]+)"); | 
					
						
							|  |  |  |     QRegExp heightRE("NAXIS2 *= *([0-9]+)"); | 
					
						
							|  |  |  |     QRegExp bitsPerPixelRE("BITPIX *= *(-?[0-9]+)"); | 
					
						
							|  |  |  |     QRegExp bzeroRE("BZERO *= *([0-9]+)"); | 
					
						
							|  |  |  |     QRegExp bscaleRE("BSCALE *= *(-?[0-9]+(.[0-9]+)?)"); | 
					
						
							|  |  |  |     QRegExp buintRE("BUNIT *= *\\'([A-Z ]+)\\'"); | 
					
						
							|  |  |  |     QRegExp cdelt1RE("CDELT1 *= *(-?[0-9]+(.[0-9]+)?)"); | 
					
						
							|  |  |  |     QRegExp cdelt2RE("CDELT2 *= *(-?[0-9]+(.[0-9]+)?)"); | 
					
						
							|  |  |  |     QRegExp endRE("END {77}"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (widthRE.indexIn(header) != -1) | 
					
						
							|  |  |  |         m_width = widthRE.capturedTexts()[1].toInt(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         qWarning() << "FITS: NAXIS1 missing"; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (heightRE.indexIn(header) != -1) | 
					
						
							|  |  |  |         m_height = heightRE.capturedTexts()[1].toInt(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         qWarning() << "FITS: NAXIS2 missing"; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (bitsPerPixelRE.indexIn(header) != -1) | 
					
						
							|  |  |  |         m_bitsPerPixel = bitsPerPixelRE.capturedTexts()[1].toInt(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         qWarning() << "FITS: BITPIX missing"; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     m_bytesPerPixel = abs(m_bitsPerPixel)/8; | 
					
						
							|  |  |  |     if (bzeroRE.indexIn(header) != -1) | 
					
						
							|  |  |  |         m_bzero = bzeroRE.capturedTexts()[1].toInt(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         m_bzero = 0; | 
					
						
							|  |  |  |     if (bscaleRE.indexIn(header) != -1) | 
					
						
							|  |  |  |         m_bscale = bscaleRE.capturedTexts()[1].toDouble(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         m_bscale = 1.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (cdelt1RE.indexIn(header) != -1) | 
					
						
							|  |  |  |         m_cdelta1 = cdelt1RE.capturedTexts()[1].toDouble(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         m_cdelta1 = 0.0; | 
					
						
							|  |  |  |     if (cdelt2RE.indexIn(header) != -1) | 
					
						
							|  |  |  |         m_cdelta2 = cdelt2RE.capturedTexts()[1].toDouble(); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         m_cdelta2 = 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (buintRE.indexIn(header) != -1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_buint = buintRE.capturedTexts()[1].trimmed(); | 
					
						
							|  |  |  |         if (m_buint.contains("MILLI")) | 
					
						
							|  |  |  |             m_uintScale = 0.001f; | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             m_uintScale = 1.0f; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         m_uintScale = 1.0f; | 
					
						
							|  |  |  |     int endIdx = endRE.indexIn(header); | 
					
						
							|  |  |  |     if (endIdx == -1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         qWarning() << "FITS: END missing"; | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     m_dataStart = ((endIdx + m_headerSize) / m_headerSize) * m_headerSize; | 
					
						
							|  |  |  |     m_valid = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 11:07:56 +01:00
										 |  |  | float FITS::value(int x, int y) const | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     int offset = m_dataStart + (m_height-1-y) * m_width * m_bytesPerPixel + x * m_bytesPerPixel; | 
					
						
							|  |  |  |     const uchar *data = (const uchar *)m_data.data(); | 
					
						
							|  |  |  |     // Big-endian
 | 
					
						
							|  |  |  |     int v = 0; | 
					
						
							|  |  |  |     for (int i = m_bytesPerPixel - 1; i >= 0; i--) | 
					
						
							|  |  |  |         v += data[offset++] << (i*8); | 
					
						
							|  |  |  |     if (m_bitsPerPixel > 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Sign-extend
 | 
					
						
							|  |  |  |         switch (m_bytesPerPixel) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         case 1: | 
					
						
							|  |  |  |             v = (char)v; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 2: | 
					
						
							|  |  |  |             v = (qint16)v; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case 3: | 
					
						
							|  |  |  |             v = (qint32)v; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return v * m_bscale + m_bzero; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2021-01-29 16:08:32 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         // Type-punning via unions apparently undefined behaviour in C++
 | 
					
						
							|  |  |  |         uint32_t i = (uint32_t)v; | 
					
						
							|  |  |  |         float f; | 
					
						
							|  |  |  |         memcpy(&f, &i, sizeof(f)); | 
					
						
							|  |  |  |         return f; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 11:07:56 +01:00
										 |  |  | float FITS::scaledValue(int x, int y) const | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     float v = value(x, y); | 
					
						
							|  |  |  |     return v * m_uintScale; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 11:07:56 +01:00
										 |  |  | int FITS::mod(int a, int b) const | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     return a - b * floor(a/(double)b); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-12 11:07:56 +01:00
										 |  |  | float FITS::scaledWrappedValue(int x, int y) const | 
					
						
							| 
									
										
										
										
											2021-01-29 12:57:58 +00:00
										 |  |  | { | 
					
						
							|  |  |  |     float v = value(mod(x, m_width), mod(y, m_height)); | 
					
						
							|  |  |  |     return v * m_uintScale; | 
					
						
							|  |  |  | } |