├── .clang-format ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .vscode └── settings.json ├── AppRun ├── CMakeLists.txt ├── CONTRIBUTING.md ├── Doxyfile.in ├── LICENSE ├── LeanHRPT-Decode.desktop ├── LeanHRPT-Decode.rc ├── README.md ├── calibration.ini ├── cmake └── mingw-w64-x86_64.cmake ├── docker ├── build.sh ├── debian_bullseye │ └── Dockerfile └── ubuntu_jammy │ └── Dockerfile ├── gradients.ini ├── images ├── .gitattributes ├── fy.webp ├── gui.webp ├── ir.webp └── metop.webp ├── logo.svg ├── logo128.ico ├── presets.ini ├── projection.ini ├── src ├── commandline.cpp ├── commandline.h ├── config │ ├── config.h │ ├── gradient.h │ ├── inipp.h │ └── preset.h ├── decoders │ ├── common │ │ ├── aip.h │ │ ├── amsu.h │ │ └── tip.h │ ├── decoder.cpp │ ├── decoder.h │ ├── fengyun_hrpt.cpp │ ├── fengyun_hrpt.h │ ├── meteor_hrpt.cpp │ ├── meteor_hrpt.h │ ├── meteor_lrpt.cpp │ ├── meteor_lrpt.h │ ├── metop_hrpt.cpp │ ├── metop_hrpt.h │ ├── noaa_dsb.h │ ├── noaa_gac.cpp │ ├── noaa_gac.h │ ├── noaa_hrpt.cpp │ └── noaa_hrpt.h ├── fingerprint.cpp ├── fingerprint.h ├── geo │ ├── crs.cpp │ ├── crs.h │ ├── geodetic.h │ ├── geolocation.cpp │ ├── geolocation.h │ ├── matrix.h │ └── vector.h ├── geometry.cpp ├── geometry.h ├── image │ ├── calibration.cpp │ ├── calibration.h │ ├── compositor.cpp │ ├── compositor.h │ ├── raw.cpp │ └── raw.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── map.cpp ├── map.h ├── network.cpp ├── network.h ├── orbit.h ├── projectdialog.cpp ├── projectdialog.h ├── projection.cpp ├── projection.h ├── protocol │ ├── ccsds │ │ ├── deframer.cpp │ │ ├── deframer.h │ │ ├── demuxer.cpp │ │ └── demuxer.h │ ├── deframer.cpp │ ├── deframer.h │ ├── lrpt │ │ ├── huffman.cpp │ │ ├── huffman.h │ │ ├── jpeg.cpp │ │ ├── jpeg.h │ │ ├── packet.cpp │ │ └── packet.h │ ├── repack.cpp │ ├── repack.h │ ├── reverse.h │ └── timestamp.h ├── qt │ ├── mainwindow.ui │ ├── projectdialog.ui │ └── qpaletteview.h ├── satinfo.h └── util.h └── tools ├── CMakeLists.txt ├── bin2cadu.cpp ├── bin2raw16.cpp ├── cli.h └── vcdu2cadu.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: Google 3 | IndentWidth: 4 4 | ColumnLimit: 130 # Conserative estimate considering the size of modern displays 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: xerbo 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A description of what the bug is 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior 15 | 16 | **Expected behavior** 17 | A description of what you expected to happen 18 | 19 | **Platform** 20 | - Linux (if so please provide the distribution and version) 21 | - Windows 22 | 23 | **Version** 24 | Put the version of LeanHRPT you're running here (and compiler and environment information if building from source) 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest a feature 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the feature you would like** 11 | A concise description of what you want to happen and if possible how 12 | 13 | **Additional context** 14 | Add any other context or screenshots about the feature request here 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### C++ ### 2 | # Prerequisites 3 | *.d 4 | 5 | # Compiled Object files 6 | *.slo 7 | *.lo 8 | *.o 9 | *.obj 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Linker files 16 | *.ilk 17 | 18 | # Debugger Files 19 | *.pdb 20 | 21 | # Compiled Dynamic libraries 22 | *.so 23 | *.dylib 24 | *.dll 25 | 26 | # Fortran module files 27 | *.mod 28 | *.smod 29 | 30 | # Compiled Static libraries 31 | *.lai 32 | *.la 33 | *.a 34 | *.lib 35 | 36 | # Executables 37 | *.exe 38 | *.out 39 | *.app 40 | *.AppImage 41 | 42 | ### CMake ### 43 | CMakeLists.txt.user 44 | CMakeCache.txt 45 | CMakeFiles 46 | CMakeScripts 47 | Testing 48 | Makefile 49 | cmake_install.cmake 50 | install_manifest.txt 51 | compile_commands.json 52 | CTestTestfile.cmake 53 | _deps 54 | CMakeUserPresets.json 55 | 56 | ### CMake Patch ### 57 | # External projects 58 | *-prefix/ 59 | 60 | ### LeanHRPT ### 61 | # Images 62 | *.webp 63 | *.jpg 64 | *.png 65 | *.tif 66 | !images/* 67 | 68 | # Binary files 69 | *.bin 70 | *.cadu 71 | *.vcdu 72 | *.raw16 73 | *.hrp 74 | 75 | # CMake build directory 76 | build/ 77 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | fail_fast: false 2 | repos: 3 | - repo: https://github.com/pre-commit/pre-commit-hooks 4 | rev: v4.3.0 5 | hooks: 6 | - id: check-added-large-files 7 | - id: end-of-file-fixer 8 | - id: mixed-line-ending 9 | args: [ --fix=lf ] 10 | 11 | - repo: https://github.com/pocc/pre-commit-hooks 12 | rev: v1.3.5 13 | hooks: 14 | - id: clang-format 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "AHRPT", 4 | "AMSU-A", 5 | "AVHRR", 6 | "backscan", 7 | "CADU", 8 | "CCSDS", 9 | "CPPDU", 10 | "deframer", 11 | "demod", 12 | "demuxer", 13 | "FengYun", 14 | "HIRS", 15 | "HRPT", 16 | "Imager", 17 | "imagers", 18 | "LeanHRPT", 19 | "libpredict", 20 | "LRPT", 21 | "MetOp", 22 | "MPDU", 23 | "MSUMR", 24 | "MTVZA", 25 | "MWIR", 26 | "NOAA", 27 | "POES", 28 | "shapefile", 29 | "shapefiles", 30 | "SWIR", 31 | "syncword", 32 | "VCDU", 33 | "VCID", 34 | "VIRR", 35 | "Xerbo" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /AppRun: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export HERE="$(dirname "$(readlink -f "${0}")")" 3 | export PATH="${HERE}/usr/bin/:${PATH}" 4 | export LD_LIBRARY_PATH="${HERE}/usr/lib/" 5 | 6 | EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | cut -d "=" -f 2) 7 | exec "${EXEC}" "$@" 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | project(LeanHRPT-Decode CXX) 3 | 4 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 5 | set(CMAKE_BUILD_TYPE Release) 6 | 7 | add_subdirectory(tools) 8 | 9 | set(CMAKE_AUTOMOC ON) 10 | set(CMAKE_AUTORCC ON) 11 | set(CMAKE_AUTOUIC ON) 12 | 13 | # Generate with `find src/ | grep "\\.cpp" | sort` 14 | file(GLOB_RECURSE CXX_SOURCE_FILES 15 | src/commandline.cpp 16 | src/decoders/decoder.cpp 17 | src/decoders/fengyun_hrpt.cpp 18 | src/decoders/meteor_hrpt.cpp 19 | src/decoders/meteor_lrpt.cpp 20 | src/decoders/metop_hrpt.cpp 21 | src/decoders/noaa_gac.cpp 22 | src/decoders/noaa_hrpt.cpp 23 | src/fingerprint.cpp 24 | src/geo/crs.cpp 25 | src/geo/geolocation.cpp 26 | src/geometry.cpp 27 | src/image/calibration.cpp 28 | src/image/compositor.cpp 29 | src/image/raw.cpp 30 | src/main.cpp 31 | src/mainwindow.cpp 32 | src/map.cpp 33 | src/network.cpp 34 | src/projectdialog.cpp 35 | src/projection.cpp 36 | src/protocol/ccsds/deframer.cpp 37 | src/protocol/ccsds/demuxer.cpp 38 | src/protocol/deframer.cpp 39 | src/protocol/lrpt/huffman.cpp 40 | src/protocol/lrpt/jpeg.cpp 41 | src/protocol/lrpt/packet.cpp 42 | src/protocol/repack.cpp 43 | ) 44 | IF (WIN32) 45 | add_executable(LeanHRPT-Decode WIN32 ${CXX_SOURCE_FILES} LeanHRPT-Decode.rc) 46 | ELSE() 47 | add_executable(LeanHRPT-Decode ${CXX_SOURCE_FILES}) 48 | ENDIF() 49 | 50 | set_property(TARGET LeanHRPT-Decode PROPERTY CXX_EXTENSIONS OFF) 51 | set_property(TARGET LeanHRPT-Decode PROPERTY CXX_STANDARD 14) 52 | 53 | target_include_directories(LeanHRPT-Decode PUBLIC src) 54 | 55 | target_compile_options(LeanHRPT-Decode PRIVATE -Wall -Wextra -Wpedantic) 56 | 57 | if (USE_ADDRESS_SANITIZER) 58 | target_compile_options(LeanHRPT-Decode PRIVATE -fsanitize=address -fno-omit-frame-pointer -g) 59 | target_link_options(LeanHRPT-Decode PRIVATE -fsanitize=address) 60 | endif() 61 | 62 | find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets REQUIRED) 63 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets REQUIRED) 64 | find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Network REQUIRED) 65 | target_link_libraries(LeanHRPT-Decode PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) 66 | target_link_libraries(LeanHRPT-Decode PRIVATE Qt${QT_VERSION_MAJOR}::Network) 67 | 68 | find_library(MUPARSER_PATH NAMES muparser NO_CACHE REQUIRED) 69 | target_link_libraries(LeanHRPT-Decode PUBLIC ${MUPARSER_PATH}) 70 | find_library(LIBPREDICT_PATH NAMES predict NO_CACHE REQUIRED) 71 | target_link_libraries(LeanHRPT-Decode PUBLIC ${LIBPREDICT_PATH}) 72 | find_library(SHAPELIB_PATH NAMES shp NO_CACHE REQUIRED) 73 | target_link_libraries(LeanHRPT-Decode PUBLIC ${SHAPELIB_PATH}) 74 | 75 | find_package(OpenMP) 76 | if(OpenMP_CXX_FOUND) 77 | target_link_libraries(LeanHRPT-Decode PRIVATE OpenMP::OpenMP_CXX) 78 | endif() 79 | 80 | # Get version from git 81 | find_package(Git) 82 | if (GIT_FOUND) 83 | execute_process(COMMAND ${GIT_EXECUTABLE} describe --tag WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} OUTPUT_VARIABLE GIT_TAG OUTPUT_STRIP_TRAILING_WHITESPACE) 84 | set(VERSION "${GIT_TAG}") 85 | else() 86 | set(VERSION "Unknown") 87 | endif() 88 | target_compile_definitions(LeanHRPT-Decode PRIVATE "VERSION=\"${VERSION}\"") 89 | 90 | # Binaries 91 | install(TARGETS LeanHRPT-Decode RUNTIME DESTINATION bin) 92 | # Desktop file and icon 93 | install(FILES ${CMAKE_SOURCE_DIR}/LeanHRPT-Decode.desktop DESTINATION share/applications) 94 | install(FILES ${CMAKE_SOURCE_DIR}/logo128.ico DESTINATION share/icons/hicolor/128x128/apps RENAME LeanHRPT-Decode.png) 95 | install(FILES ${CMAKE_SOURCE_DIR}/logo.svg DESTINATION share/icons/hicolor/scalable/apps RENAME LeanHRPT-Decode.svg) 96 | configure_file(logo128.ico logo128.ico COPYONLY) 97 | configure_file(logo.svg logo.svg COPYONLY) 98 | # Config files 99 | install(FILES ${CMAKE_SOURCE_DIR}/projection.ini DESTINATION share/leanhrpt) 100 | install(FILES ${CMAKE_SOURCE_DIR}/calibration.ini DESTINATION share/leanhrpt) 101 | install(FILES ${CMAKE_SOURCE_DIR}/presets.ini DESTINATION share/leanhrpt) 102 | install(FILES ${CMAKE_SOURCE_DIR}/gradients.ini DESTINATION share/leanhrpt) 103 | configure_file(projection.ini projection.ini COPYONLY) 104 | configure_file(calibration.ini calibration.ini COPYONLY) 105 | configure_file(presets.ini presets.ini COPYONLY) 106 | configure_file(gradients.ini gradients.ini COPYONLY) 107 | 108 | # Package information 109 | string(SUBSTRING ${VERSION} 1 -1 CPACK_PACKAGE_VERSION) 110 | set(CPACK_PACKAGE_NAME "LeanHRPT-Decode") 111 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "An easy to use HRPT decoder") 112 | set(CPACK_PACKAGE_DESCRIPTION "LeanHRPT is an easy to use and powerful tool for the manipulation of Level 0 HRPT data.") 113 | set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/Xerbo/LeanHRPT-Decode") 114 | set(CPACK_PACKAGE_CONTACT "Xerbo (xerbo@protonmail.com)") 115 | # Debian 116 | set(CPACK_GENERATOR "DEB") 117 | set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64") 118 | set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) 119 | set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "gdal-bin") 120 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libpredict") 121 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") 122 | 123 | # Only allow packaging if we know what we are packaging 124 | if (NOT VERSION STREQUAL "Unknown") 125 | include(CPack) 126 | endif() 127 | 128 | option(BUILD_DOXYGEN "Create HTML/LaTeX documentation with Doxygen" OFF) 129 | 130 | if (BUILD_DOXYGEN) 131 | find_package(Doxygen) 132 | set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) 133 | set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) 134 | configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) 135 | 136 | add_custom_target(doc_doxygen ALL 137 | COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} 138 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 139 | COMMENT "Generating documentation with Doxygen" 140 | VERBATIM) 141 | endif() 142 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | Thanks for showing an interest in improving LeanHRPT, these guidelines outline how you should go about contributing to the project. 4 | 5 | ## Did you find a bug? 6 | 7 | 1. Make sure the bug has not already been reported. 8 | 2. If an issue already exists, leave a thumbs up to "bump" the issue 9 | 3. If an issue doesn't exist, open a new one with a clear title and description and, if relevant, any files that cause the behaviour 10 | 11 | ## Do you have a patch that fixes a bug/adds a feature? 12 | 13 | 1. Fork this repository and put your changes on a *new branch*. 14 | 2. Make sure to use sensible commit names (no "Oops" commits) 15 | 3. Open a new pull request into master. 16 | 17 | If you don't have a GitHub account, you can email me the patch at `xerbo (at) protonmail (dot) com` 18 | 19 | ## Coding style 20 | 21 | The coding style of LeanHRPT is based off the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) with minor modifications (see `.clang-format`), in addition to this all files should use LF line endings and end in a newline. Use American english (i.e. color, not colour). 22 | 23 | To automate this LeanHRPT comes with [pre-commit](https://pre-commit.com/) hooks which will enforce these rules. 24 | 25 | ### Development environment 26 | 27 | LeanHRPT is developed with VSCode with the following extensions, while not required they may make your life easier: 28 | 29 | - [C/C++](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) 30 | - [CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) 31 | - [CMake](https://marketplace.visualstudio.com/items?itemName=twxs.cmake) 32 | - [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) 33 | - [Clang-Format](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) 34 | 35 | ## Commit message style 36 | 37 | - Keep titles short to prevent wrapping (descriptions exist) 38 | - Split up large changes into multiple commits 39 | - Never use hastags for sequential counting in commits, as this interferes with issue/PR referencing 40 | -------------------------------------------------------------------------------- /LeanHRPT-Decode.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Name=LeanHRPT Decode 4 | Comment=An easy to use HRPT decoder 5 | Categories=HamRadio;Science 6 | Exec=LeanHRPT-Decode 7 | Icon=LeanHRPT-Decode 8 | -------------------------------------------------------------------------------- /LeanHRPT-Decode.rc: -------------------------------------------------------------------------------- 1 | 1 ICON "logo128.ico" 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LeanHRPT Decode 2 | 3 | Screenshot of LeanHRPT's GUI 4 | 5 | [![Build](https://github.com/Xerbo/LeanHRPT-Decode/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/Xerbo/LeanHRPT-Decode/actions/workflows/build.yml) 6 | 7 | LeanHRPT is an easy to use and powerful tool for the manipulation of Level 0 HRPT data. Also see [LeanHRPT Demod](https://github.com/Xerbo/LeanHRPT-Demod). 8 | 9 | ## Installation 10 | 11 | Compiled builds for Linux and Windows are available from the [releases](https://github.com/Xerbo/LeanHRPT-Decode/releases) page. 12 | 13 | ## Usage 14 | 15 | See the [wiki](https://github.com/Xerbo/LeanHRPT-Decode/wiki). 16 | 17 | ## Example results 18 | 19 | |FengYun-3B with the NCSWIR preset|MetOp-C SWIR with a map overlay|Projected NOAA IR image| 20 | |-|-|-| 21 | |![](images/fy.webp)|![](images/metop.webp)|![](images/ir.webp)| 22 | 23 | ## Advanced 24 | 25 | ### Building from source 26 | 27 | You will need Qt (at least 5.14), [`muparser`](https://github.com/beltoforion/muparser), [`libpredict`](https://github.com/la1k/libpredict) and [`shapelib`](https://github.com/OSGeo/shapelib) installed. 28 | 29 | While MacOS isn't officially supported, compiling should be pretty similar to the steps below. 30 | 31 | #### Dependencies Installation 32 | 33 | ```sh 34 | # Debian/Ubuntu 35 | sudo apt install cmake gcc g++ qtbase5-dev libmuparser-dev libshp-dev 36 | git clone -b v2.0.0 https://github.com/la1k/libpredict && cd libpredict 37 | mkdir build && cd build 38 | cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release .. 39 | cmake --build . 40 | sudo cmake --install . 41 | ``` 42 | 43 | ```sh 44 | # Fedora 45 | sudo dnf install g++ cmake qt5-qtbase-devel muParser-devel shapelib-devel 46 | git clone -b v2.0.0 https://github.com/la1k/libpredict && cd libpredict 47 | mkdir build && cd build 48 | cmake -DCMAKE_BUILD_TYPE=Release .. 49 | cmake --build . 50 | sudo cmake --install . 51 | ``` 52 | 53 | ```sh 54 | # MSYS2 55 | pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-qt5-base mingw-w64-x86_64-muparser mingw-w64-x86_64-shapelib mingw-w64-x86_64-ninja git 56 | git clone -b v2.0.0 https://github.com/la1k/libpredict && cd libpredict 57 | mkdir build && cd build 58 | cmake -DCMAKE_INSTALL_PREFIX=/mingw64 -DCMAKE_BUILD_TYPE=Release .. 59 | cmake --build . 60 | cmake --install . 61 | ``` 62 | 63 | #### Development Setup 64 | 65 | ```sh 66 | mkdir build && cd build 67 | cmake .. 68 | cmake --build . 69 | ``` 70 | 71 | ### Input file format 72 | 73 | Input files should be: 74 | 75 | - NOAA - raw data, raw16 or HRP 76 | - Meteor - raw data 77 | - MetOp - CADUs or VCDUs 78 | - FengYun - CADUs or VCDUs 79 | 80 | ## Credits 81 | 82 | - [`MeteorDemod`](https://github.com/Digitelektro/MeteorDemod) - geotransformation code 83 | - [JM (@g5juergenm)](https://twitter.com/g5juergenm) - testing and feedback 84 | - KentuckyFriedData - donation 85 | -------------------------------------------------------------------------------- /calibration.ini: -------------------------------------------------------------------------------- 1 | # 2 | # LeanHRPT calibration coefficients file 3 | # 4 | # This file contains the coefficients used to calibrate channels, 5 | # currently it supports 3 types of calibration: 6 | # linear (VIRR and MSU-MR, default) 7 | # split_linear (AVHRR) 8 | # radiance (IR) 9 | # 10 | # Sources: 11 | # https://www.ospo.noaa.gov/data/ppp/notice_files/aug1021.html 12 | # http://www.nsmc.org.cn/en/NSMC/Contents/100089.html 13 | # https://www.eumetsat.int/media/49299 14 | # https://www.eumetsat.int/media/49298 15 | # NOAA KLM User's Guide, August 2014 Revision 16 | # 17 | 18 | [MetOp-A_AVHRR/1] 19 | type = split_linear 20 | a1 = 0.05747 21 | b1 = -2.324 22 | a2 = 0.1698 23 | b2 = -58.62 24 | c = 501.01 25 | [MetOp-A_AVHRR/2] 26 | type = split_linear 27 | a1 = 0.07261 28 | b1 = -2.886 29 | a2 = 0.2157 30 | b2 = -74.47 31 | c = 500.42 32 | [MetOp-A_AVHRR/3] 33 | type = split_linear 34 | a1 = 0.03539 35 | b1 = -1.454 36 | a2 = 0.2458 37 | b2 = -107.07 38 | c = 501.94 39 | 40 | [MetOp-B_AVHRR/1] 41 | type = split_linear 42 | a1 = 0.05732 43 | b1 = -2.258 44 | a2 = 0.1698 45 | b2 = -58.58 46 | c = 500.62 47 | [MetOp-B_AVHRR/2] 48 | type = split_linear 49 | a1 = 0.06435 50 | b1 = -2.598 51 | a2 = 0.1896 52 | b2 = -65.58 53 | c = 503.00 54 | [MetOp-B_AVHRR/3] 55 | type = split_linear 56 | a1 = 0.03290 57 | b1 = -1.382 58 | a2 = 0.2291 59 | b2 = -99.67 60 | c = 500.85 61 | [MetOp-B_AVHRR/4] 62 | type = radiance 63 | ns = -4.75 64 | b0 = 4.85 65 | b1 = -0.096771 66 | b2 = 0.00048091 67 | vc = 933.63 68 | a = 0.504183 69 | b = 0.998638 70 | [MetOp-B_AVHRR/5] 71 | type = radiance 72 | ns = -4.39 73 | b0 = 4.36 74 | b1 = -0.076635 75 | b2 = 0.00033524 76 | vc = 839.62 77 | a = 0.381279 78 | b = 0.998861 79 | 80 | [MetOp-C_AVHRR/1] 81 | type = split_linear 82 | a1 = 0.05506 83 | b1 = -2.223 84 | a2 = 0.1613 85 | b2 = -55.21 86 | c = 498.68 87 | [MetOp-C_AVHRR/2] 88 | type = split_linear 89 | a1 = 0.06351 90 | b1 = -2.601 91 | a2 = 0.1861 92 | b2 = -63.88 93 | c = 500.01 94 | [MetOp-C_AVHRR/3] 95 | type = split_linear 96 | a1 = 0.03269 97 | b1 = -1.326 98 | a2 = 0.2300 99 | b2 = -99.73 100 | c = 498.72 101 | [MetOp-C_AVHRR/4] 102 | type = radiance 103 | ns = -6.27 104 | b0 = 6.58 105 | b1 = -0.132030 106 | b2 = 0.00065922 107 | vc = 930.31 108 | a = 0.5899 109 | b = 0.998403 110 | [MetOp-C_AVHRR/5] 111 | type = radiance 112 | ns = -2.55 113 | b0 = 3.23 114 | b1 = -0.056920 115 | b2 = 0.00024963 116 | vc = 828.6 117 | a = 0.3781050 118 | b = 0.998854 119 | 120 | [NOAA-15_AVHRR/1] 121 | type = split_linear 122 | a1 = 0.05680 123 | b1 = -2.187 124 | a2 = 0.1633 125 | b2 = -54.99 126 | c = 496.00 127 | [NOAA-15_AVHRR/2] 128 | type = split_linear 129 | a1 = 0.05960 130 | b1 = -2.410 131 | a2 = 0.1629 132 | b2 = -55.24 133 | c = 511.00 134 | [NOAA-15_AVHRR/3] 135 | type = radiance 136 | ns = 0.0 137 | b0 = 0.0 138 | b1 = 0.0 139 | b2 = 0.0 140 | vc = 2695.9743 141 | a = 1.621256 142 | b = 0.998015 143 | [NOAA-15_AVHRR/4] 144 | type = radiance 145 | ns = -4.50 146 | b0 = 4.76 147 | b1 = -0.0932 148 | b2 = 0.0004524 149 | vc = 925.4075 150 | a = 0.337810 151 | b = 0.998719 152 | [NOAA-15_AVHRR/5] 153 | type = radiance 154 | ns = -3.61 155 | b0 = 3.83 156 | b1 = -0.0659 157 | b2 = 0.0002811 158 | vc = 839.8979 159 | a = 0.304558 160 | b = 0.999024 161 | 162 | [NOAA-18_AVHRR/1] 163 | type = split_linear 164 | a1 = 0.06168 165 | b1 = -2.432 166 | a2 = 0.1839 167 | b2 = -63.25 168 | c = 501.54 169 | [NOAA-18_AVHRR/2] 170 | type = split_linear 171 | a1 = 0.07524 172 | b1 = -2.964 173 | a2 = 0.2258 174 | b2 = -78.66 175 | c = 500.40 176 | [NOAA-18_AVHRR/3] 177 | type = radiance 178 | ns = 0.0 179 | b0 = 0.0 180 | b1 = 0.0 181 | b2 = 0.0 182 | vc = 2659.7952 183 | a = 1.698704 184 | b = 0.996960 185 | [NOAA-18_AVHRR/4] 186 | type = radiance 187 | ns = -5.53 188 | b0 = 5.82 189 | b1 = -0.11069 190 | b2 = 0.00052337 191 | vc = 928.1460 192 | a = 0.436645 193 | b = 0.998607 194 | [NOAA-18_AVHRR/5] 195 | type = radiance 196 | ns = -2.22 197 | b0 = 2.67 198 | b1 = -0.04360 199 | b2 = 0.00017715 200 | vc = 833.2532 201 | a = 0.253179 202 | b = 0.999057 203 | 204 | [NOAA-19_AVHRR/1] 205 | type = split_linear 206 | a1 = 0.05571 207 | b1 = -2.165 208 | a2 = 0.1643 209 | b2 = -56.49 210 | c = 496.43 211 | [NOAA-19_AVHRR/2] 212 | type = split_linear 213 | a1 = 0.06649 214 | b1 = -2.579 215 | a2 = 0.1981 216 | b2 = -68.37 217 | c = 500.37 218 | [NOAA-19_AVHRR/3] 219 | type = radiance 220 | ns = 0.0 221 | b0 = 0.0 222 | b1 = 0.0 223 | b2 = 0.0 224 | vc = 2670.0 225 | a = 1.67396 226 | b = 0.997364 227 | [NOAA-19_AVHRR/4] 228 | type = radiance 229 | ns = -5.49 230 | b0 = 5.70 231 | b1 = -0.11187 232 | b2 = 0.00054668 233 | vc = 928.9 234 | a = 0.53959 235 | b = 0.998534 236 | [NOAA-19_AVHRR/5] 237 | type = radiance 238 | ns = -3.39 239 | b0 = 3.58 240 | b1 = -0.05991 241 | b2 = 0.00024985 242 | vc = 831.9 243 | a = 0.36064 244 | b = 0.998913 245 | 246 | [FengYun-3B_VIRR/1] 247 | a = 0.1264 248 | b = -1.4320 249 | [FengYun-3B_VIRR/2] 250 | a = 0.1353 251 | b = -1.6236 252 | [FengYun-3B_VIRR/6] 253 | a = 0.0919 254 | b = -2.4821 255 | [FengYun-3B_VIRR/7] 256 | a = 0.0938 257 | b = -1.1494 258 | [FengYun-3B_VIRR/8] 259 | a = 0.0857 260 | b = -1.0280 261 | [FengYun-3B_VIRR/9] 262 | a = 0.0803 263 | b = -0.9636 264 | [FengYun-3B_VIRR/10] 265 | a = 0.0630 266 | b = -0.7628 267 | 268 | [FengYun-3C_VIRR/1] 269 | a = 0.1264 270 | b = -1.4320 271 | [FengYun-3C_VIRR/2] 272 | a = 0.1353 273 | b = -1.6236 274 | [FengYun-3C_VIRR/6] 275 | a = 0.0919 276 | b = -2.4821 277 | [FengYun-3C_VIRR/7] 278 | a = 0.0938 279 | b = -1.1494 280 | [FengYun-3C_VIRR/8] 281 | a = 0.0857 282 | b = -1.0280 283 | [FengYun-3C_VIRR/9] 284 | a = 0.0803 285 | b = -0.9636 286 | [FengYun-3C_VIRR/10] 287 | a = 0.0630 288 | b = -0.7628 289 | -------------------------------------------------------------------------------- /cmake/mingw-w64-x86_64.cmake: -------------------------------------------------------------------------------- 1 | # Sample toolchain file for building for Windows from an Ubuntu Linux system. 2 | # 3 | # Typical usage: 4 | # *) install cross compiler: `sudo apt-get install mingw-w64` 5 | # *) cd build 6 | # *) cmake -DCMAKE_TOOLCHAIN_FILE=~/mingw-w64-x86_64.cmake .. 7 | 8 | set(CMAKE_SYSTEM_NAME Windows) 9 | set(TOOLCHAIN_PREFIX x86_64-w64-mingw32) 10 | 11 | # cross compilers to use for C, C++ and Fortran 12 | set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) 13 | set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) 14 | set(CMAKE_Fortran_COMPILER ${TOOLCHAIN_PREFIX}-gfortran) 15 | set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) 16 | 17 | # target environment on the build host system 18 | set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) 19 | 20 | # modify default behavior of FIND_XXX() commands 21 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 22 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 24 | -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /root 3 | git clone https://github.com/la1k/libpredict.git && cd libpredict 4 | mkdir build && cd build 5 | cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release .. 6 | make -j4 7 | make install 8 | 9 | cd /root/LeanHRPT-Decode 10 | mkdir build && cd build 11 | git config --global --add safe.directory /root/LeanHRPT-Decode 12 | cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release .. 13 | make -j4 14 | make package 15 | -------------------------------------------------------------------------------- /docker/debian_bullseye/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bullseye 2 | COPY build.sh /root 3 | RUN chmod +x /root/build.sh 4 | 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | ENV TZ=Etc/UTC 7 | RUN apt-get update 8 | RUN apt-get install -y git cmake g++ qtbase5-dev libmuparser-dev file dpkg-dev libshp-dev 9 | -------------------------------------------------------------------------------- /docker/ubuntu_jammy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:jammy 2 | COPY build.sh /root 3 | RUN chmod +x /root/build.sh 4 | 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | ENV TZ=Etc/UTC 7 | RUN apt-get update 8 | RUN apt-get install -y git cmake g++ qtbase5-dev libmuparser-dev file dpkg-dev libshp-dev 9 | -------------------------------------------------------------------------------- /gradients.ini: -------------------------------------------------------------------------------- 1 | [IronBow] 2 | author = FLIR 3 | map = 01083E,260277,530297,7B03A3,9F07A0,BA188B,D12C70,E44450,F25E30,FA7A13,FC9504,FAAF03,FBC909,FBDF29,FBEF63,F5FCBF 4 | [Lava] 5 | author = FLIR 6 | map = 0D1A44,1A4599,0760A3,03719D,028091,148186,64477B,95275D,B51D3C,D01B27,F33024,FC4B12,FD7402,FBA802,FDCE09,FEED9A 7 | [Artic] 8 | author = FLIR 9 | map = 0101AC,0105EF,043FFC,1999FB,2BDEFA,42E0E3,54B7BB,5B7F80,73635B,A3633D,CB6322,F66706,FD8D05,FCC604,FDE040,FEF1B0 10 | [Rainbow] 11 | author = FLIR 12 | map = 01054E,01297A,0246A6,045ECF,037AD3,169491,5EB42B,A9CC03,DED403,F4C709,FBA313,FD5F2A,FA1F3E,F73959,FA807C,FDC8AE 13 | [NDVI] 14 | author = NASA 15 | map = E3D6C6,E3D6C6,E3D6C6,E3D6C6,E3D6C6,E3D6C6,E3D6C6,E3D6C6,E3D6C6,E3D6C6,CFC0A2,B9AC7C,A19B52,85892E,6B771F,526321,3C4E20,263F08,132D02,132D02 16 | -------------------------------------------------------------------------------- /images/.gitattributes: -------------------------------------------------------------------------------- 1 | fy.webp filter=lfs diff=lfs merge=lfs -text 2 | ir.webp filter=lfs diff=lfs merge=lfs -text 3 | metop.webp filter=lfs diff=lfs merge=lfs -text 4 | mhs.png filter=lfs diff=lfs merge=lfs -text 5 | gui.webp filter=lfs diff=lfs merge=lfs -text 6 | -------------------------------------------------------------------------------- /images/fy.webp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7a27173662024bded4b6d917a741acbd1a1338a4b3ec5e59c307d4d76478e130 3 | size 308312 4 | -------------------------------------------------------------------------------- /images/gui.webp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:5819307f370e14aa93312410ee6f034d639f975c76ca59db5c921641d5461788 3 | size 181446 4 | -------------------------------------------------------------------------------- /images/ir.webp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:6dafe5d26c2e76648250bee90703eab2906a2ac00207bdd54c2d9e698e0c47fa 3 | size 147544 4 | -------------------------------------------------------------------------------- /images/metop.webp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d007f4d75f1009a2ee8bc1b8bcc6b5ad17818708fd5c92f3303fa3f6a9035701 3 | size 1063796 4 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 18 | 23 | 29 | 34 | 39 | 45 | 46 | 49 | 54 | 60 | 65 | 70 | 76 | 77 | 78 | 80 | 81 | 83 | image/svg+xml 84 | 86 | 87 | 88 | 89 | 90 | 93 | 96 | 101 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /logo128.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Xerbo/LeanHRPT-Decode/3801360ea22d8e39d81c67d7a9b09a5479b43ebc/logo128.ico -------------------------------------------------------------------------------- /presets.ini: -------------------------------------------------------------------------------- 1 | # 2 | # LeanHRPT preset definition file 3 | # 4 | # It is formatted as ini and parsed by inipp (https://github.com/mcmtroffaes/inipp) 5 | # meaning that features like variables are supported. 6 | # 7 | # Presets for uncalibrated imagers will not function reliably over multiple files 8 | 9 | [Visible Color] 10 | description = Visible light band composite (daylight only) 11 | imagers = VIRR 12 | expression:VIRR = ch1,ch9,ch7 13 | 14 | [Natural Color (NIR)] 15 | description = Natural color using a red and near-infrared band (daylight only) 16 | imagers = AVHRR|MSU-MR|VIRR 17 | expression:AVHRR = ch2*0.6 + min(ch1,ch2)*0.4, ch2, ch1 18 | expression:MSU-MR = ch2*0.6 + min(ch1,ch2)*0.4, ch2, ch1 19 | expression:VIRR = ch2*0.6 + min(ch1,ch2)*0.4, ch2, ch1 20 | 21 | [Natural Color (SWIR)] 22 | description = Natural color using a red, near-infrared, and shortwave-infrared band (daylight only) 23 | imagers = MSU-MR|AVHRR|VIRR 24 | expression:AVHRR = 0.6*max(SWIR,min(ch2,ch1)) + 0.4*ch2, ch2, ch1 25 | expression:MSU-MR = 0.6*max(SWIR,min(ch2,ch1)) + 0.4*ch2, ch2, ch1 26 | expression:VIRR = 0.6*max(SWIR,min(ch2,ch1)) + 0.4*ch2, ch2, ch1 27 | 28 | [False Color (NIR)] 29 | description = Combination of red and near-infrared bands (daylight only) 30 | imagers = AVHRR|MSU-MR|VIRR 31 | expression:AVHRR = ch2,ch1,ch1 32 | expression:MSU-MR = ch2,ch1,ch1 33 | expression:VIRR = ch2,ch1,ch1 34 | 35 | [False Color (SWIR)] 36 | description = Combination of red, near-infrared, and shortwave-infrared bands (daylight only) 37 | imagers = AVHRR|MSU-MR|VIRR 38 | expression:AVHRR = ch3,ch2,ch1 39 | expression:MSU-MR = ch3,ch2,ch1 40 | expression:VIRR = ch6,ch2,ch1 41 | 42 | [False Color (LWIR)] 43 | description = Combination of red, near-infrared, and longwave-infrared bands (daylight only) 44 | imagers = AVHRR|MSU-MR|VIRR 45 | expression:AVHRR = ch1,ch2,ch4 46 | expression:MSU-MR = ch1,ch2,ch5 47 | expression:VIRR = ch1,ch2,ch4 48 | 49 | [NDVI] 50 | description = Normalized difference vegetation index 51 | imagers = AVHRR|MSU-MR|VIRR 52 | expression:AVHRR = (ch2-ch1)/(ch2+ch1) 53 | expression:MSU-MR = (ch2-ch1)/(ch2+ch1) 54 | expression:VIRR = (ch2-ch1)/(ch2+ch1) 55 | 56 | [NDVI Color] 57 | description = Colorized normalized difference vegetation index 58 | imagers = AVHRR|VIRR 59 | expression:AVHRR = ch2,max(ch2,((ch2-ch1)/(ch2+ch1))),ch1 60 | expression:VIRR = ch2,max(ch2,((ch2-ch1)/(ch2+ch1))),ch1 61 | # NDVI Color doesn't work with MSU-MR 62 | 63 | [Thermal Infrared] 64 | description = Longwave-infrared band (thermal, ~11000 nm) 65 | imagers = AVHRR|MSU-MR|VIRR|HIRS 66 | expression:AVHRR = ch4 67 | expression:MSU-MR = ch5 68 | expression:VIRR = ch4 69 | expression:HIRS = ch8 70 | 71 | [Volcanic Ash] 72 | description = Difference between 10.8 and 12.0 micron bands 73 | imagers = AVHRR|VIRR|MSU-MR|HIRS 74 | expression:AVHRR = ch4-ch5 75 | expression:VIRR = ch4-ch5 76 | expression:MSU-MR = ch5-ch6 77 | expression:HIRS = ch8-ch10 78 | 79 | [False Color (Land)] 80 | description = Basic false color, using any bands that achieve an approximation of land color 81 | imagers = HIRS|MHS|MTVZA|AMSU-A 82 | expression:MHS = ch1, ch1, ch2 83 | expression:HIRS = ch20, ch20, ch19 84 | expression:MTVZA = ch6*0.9+ch10*0.1, ch6, ch10 85 | expression:AMSU-A = ch1*0.6 + min(ch4,ch1)*0.4, ch2, ch4 86 | 87 | [183 GHz Water Vapor] 88 | description = False color using bands in the proximity of the 183.31 GHz water vapor peak 89 | imagers = MHS|MTVZA 90 | expression:MHS = ch3,ch4,ch5 91 | expression:MTVZA = ch28,ch29,ch30 92 | -------------------------------------------------------------------------------- /projection.ini: -------------------------------------------------------------------------------- 1 | [NOAA-15_AVHRR] 2 | fov = 55 3 | yaw = -1.6 4 | roll = -0.1 5 | pitch = 0.0 6 | toffset = -0.03425 7 | [NOAA-18_AVHRR] 8 | fov = 54.9 9 | yaw = -1.8 10 | roll = -0.18 11 | pitch = 0 12 | toffset = -0.03425 13 | [NOAA-19_AVHRR] 14 | fov = 55.2 15 | yaw = 1.9 16 | roll = -0.08 17 | pitch = 0.0 18 | toffset = -0.03425 19 | 20 | [NOAA-18_HIRS] 21 | fov = 52.2 22 | yaw = 0.0 23 | roll = 0.0 24 | pitch = 0.0 25 | toffset = 0.0 26 | [NOAA-19_HIRS] 27 | fov = 52.2 28 | yaw = 0.0 29 | roll = 0.0 30 | pitch = 0.0 31 | toffset = 0.0 32 | 33 | # NOAA-18 MHS is dead 34 | [NOAA-19_MHS] 35 | fov = 50.0 36 | yaw = 0.0 37 | roll = 0.5 38 | pitch = 0.0 39 | toffset = 0.0 40 | 41 | [NOAA-18_AMSU-A] 42 | fov = 47.0 43 | yaw = -6.0 44 | roll = 0.0 45 | pitch = 0.0 46 | toffset = -7.0 47 | [NOAA-19_AMSU-A] 48 | fov = 47.0 49 | yaw = -6.0 50 | roll = 0.0 51 | pitch = 0.0 52 | toffset = -7.0 53 | 54 | [MetOp-A_AVHRR] 55 | fov = 55.2 56 | yaw = 0.3 57 | roll = -0.2 58 | pitch = 0.3 59 | toffset = 0.0 60 | [MetOp-B_AVHRR] 61 | fov = 55 62 | yaw = 0.1 63 | roll = 0.0 64 | pitch = 0.0 65 | toffset = -0.3 66 | [MetOp-C_AVHRR] 67 | fov = 55.1 68 | yaw = 0.2 69 | roll = -0.15 70 | pitch = 0.2 71 | toffset = 0.0 72 | 73 | [MetOp-A_MHS] 74 | fov = 50.0 75 | yaw = 0.0 76 | roll = 0.5 77 | pitch = 0.0 78 | toffset = 0.5 79 | [MetOp-B_MHS] 80 | fov = 50.0 81 | yaw = 0.0 82 | roll = 0.5 83 | pitch = 0.0 84 | toffset = 0.5 85 | [MetOp-C_MHS] 86 | fov = 50.0 87 | yaw = 0.0 88 | roll = 0.5 89 | pitch = 0.0 90 | toffset = 0.5 91 | 92 | [Meteor-M2_MSU-MR] 93 | fov = 54.8 94 | yaw = -3 95 | roll = 2.3 96 | pitch = 0.0 97 | toffset = -0.1 98 | [Meteor-M22_MSU-MR] 99 | fov = 54.9 100 | yaw = -2.6 101 | roll = 0.0 102 | pitch = 0.0 103 | toffset = 0.3 104 | 105 | [Meteor-M22_MTVZA] 106 | curved = true 107 | fov = 52.0 108 | yaw = 5.0 109 | roll = 0.0 110 | pitch = -53.3 111 | pitchscale = 0.9 112 | toffset = 0.0 113 | 114 | [FengYun-3C_VIRR] 115 | fov = 55.31 116 | yaw = 2.7 117 | roll = 0.0 118 | pitch = -0.15 119 | toffset = 0.0 120 | [FengYun-3B_VIRR] 121 | fov = 55.31 122 | yaw = 2.7 123 | roll = 0.0 124 | pitch = -0.15 125 | toffset = 0.0 126 | -------------------------------------------------------------------------------- /src/commandline.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_COMMANDLINE_H_ 20 | #define LEANHRPT_COMMANDLINE_H_ 21 | 22 | #include 23 | 24 | int parseCommandLine(QCommandLineParser &parser); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/config/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_CONFIG_CONFIG_H_ 20 | #define LEANHRPT_CONFIG_CONFIG_H_ 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | // Load a config file, first try looking in the current directory and then config paths 29 | class Config : public inipp::Ini { 30 | public: 31 | Config(std::string filename) { 32 | // Local config 33 | if (try_load(filename)) return; 34 | 35 | // User config 36 | QString config = QStandardPaths::locate(QStandardPaths::ConfigLocation, QString::fromStdString("leanhrpt/" + filename)); 37 | if (!config.isEmpty() && try_load(config.toStdString())) return; 38 | 39 | #ifndef _WIN32 40 | // Internal AppImage config 41 | std::string here = std::getenv("HERE") ? std::getenv("HERE") : ""; 42 | if (!here.empty() && try_load(here + "/usr/share/leanhrpt/" + filename)) return; 43 | #endif 44 | 45 | // System config 46 | config = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString::fromStdString("leanhrpt/" + filename)); 47 | if (!config.isEmpty() && try_load(config.toStdString())) return; 48 | 49 | std::cerr << "Could not open " << filename << std::endl; 50 | } 51 | 52 | private: 53 | bool try_load(std::string filename) { 54 | std::filebuf file; 55 | if (file.open(filename, std::ios::in)) { 56 | std::istream stream(&file); 57 | parse(stream); 58 | interpolate(); 59 | file.close(); 60 | return true; 61 | } 62 | 63 | return false; 64 | } 65 | }; 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /src/config/gradient.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_CONFIG_GRADIENT_H_ 20 | #define LEANHRPT_CONFIG_GRADIENT_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "config.h" 27 | 28 | struct Gradient { 29 | std::string author; 30 | std::vector stops; 31 | }; 32 | 33 | class GradientManager { 34 | public: 35 | GradientManager() { reload(); } 36 | void reload() { 37 | Config ini("gradients.ini"); 38 | gradients.clear(); 39 | 40 | for (auto &i : ini.sections) { 41 | std::vector stops; 42 | 43 | for (const auto &x : QString::fromStdString(i.second["map"]).split(",")) { 44 | stops.push_back(QColor("#" + x)); 45 | } 46 | 47 | gradients.insert({i.first, Gradient{i.second["author"], stops}}); 48 | } 49 | } 50 | 51 | std::map gradients; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/config/preset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_CONFIG_PRESET_H 20 | #define LEANHRPT_CONFIG_PRESET_H 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "config.h" 27 | #include "satinfo.h" 28 | 29 | struct Preset { 30 | std::string description; 31 | std::string category; 32 | std::string author; 33 | std::set imagers; 34 | std::string expression; 35 | std::map overrides; 36 | }; 37 | 38 | class PresetManager { 39 | public: 40 | PresetManager() { reload(); } 41 | void reload() { 42 | Config ini("presets.ini"); 43 | presets.clear(); 44 | 45 | for (auto &i : ini.sections) { 46 | try { 47 | std::map overrides; 48 | for (const auto &x : i.second) { 49 | size_t y = x.first.find(":"); 50 | if (y != std::string::npos) { 51 | overrides.insert(std::pair(get_sensor(x.first.substr(y + 1)), x.second)); 52 | } 53 | } 54 | 55 | presets.insert(std::pair( 56 | i.first, Preset{i.second["description"], i.second["category"], i.second["author"], 57 | parse_imagers(i.second["imagers"]), i.second["expression"], overrides})); 58 | } catch (std::out_of_range &e) { 59 | std::cerr << "Syntax error in preset \"" << i.first << "\"" << std::endl; 60 | } 61 | } 62 | } 63 | 64 | std::map presets; 65 | 66 | private: 67 | std::set parse_imagers(std::string str) { 68 | std::set imagers; 69 | 70 | std::stringstream stream(str); 71 | std::string imager; 72 | while (std::getline(stream, imager, '|')) { 73 | imagers.insert(get_sensor(imager)); 74 | } 75 | 76 | return imagers; 77 | } 78 | }; 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/decoders/common/aip.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_COMMON_AIP_H_ 20 | #define LEANHRPT_DECODERS_COMMON_AIP_H_ 21 | 22 | #include 23 | #include 24 | 25 | #include "amsu.h" 26 | #include "image/raw.h" 27 | #include "protocol/repack.h" 28 | #include "satinfo.h" 29 | 30 | class AIPDecoder { 31 | public: 32 | /** 33 | * Decode a MHS Line 34 | * 35 | * @return If a line was decoded 36 | */ 37 | bool work(std::map &images, const uint8_t *frame) { 38 | uint8_t frame_counter = frame[4]; 39 | if (frame_counter >= 80) return false; 40 | 41 | std::memcpy(&mhsline[frame_counter * 50], &frame[48], 50); 42 | std::memcpy(&amsua1_line[frame_counter * 26], &frame[8], 26); 43 | std::memcpy(&amsua2_line[frame_counter * 14], &frame[34], 14); 44 | if (frame_counter == 79) { 45 | images[Imager::MHS]->push16Bit((uint16_t *)&mhsline[198], 0); 46 | images[Imager::MHS]->push16Bit((uint16_t *)&mhsline[1532], 0); 47 | images[Imager::MHS]->push16Bit((uint16_t *)&mhsline[2864], 0); 48 | amsu_decoder.work(images, amsua1_line, amsua2_line); 49 | 50 | std::memset(mhsline, 0, 80 * 50); 51 | std::memset(amsua1_line, 0, 80 * 26); 52 | std::memset(amsua2_line, 0, 80 * 14); 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | private: 59 | uint8_t amsua1_line[80 * 26]; 60 | uint8_t amsua2_line[80 * 14]; 61 | uint8_t mhsline[80 * 50]; 62 | AMSUDecoder amsu_decoder; 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/decoders/common/amsu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_COMMON_AMSU_H_ 20 | #define LEANHRPT_DECODERS_COMMON_AMSU_H_ 21 | 22 | #define AMSU_FILL_WORD 0x0001 23 | 24 | #include 25 | 26 | #include "image/raw.h" 27 | #include "protocol/repack.h" 28 | #include "satinfo.h" 29 | 30 | class AMSUDecoder { 31 | public: 32 | void work(std::map &images, const uint8_t *amsua1, const uint8_t *amsua2) { 33 | uint16_t scene[30 * 15]; 34 | uint16_t words[40 * 26]; 35 | 36 | removefill(amsua1, words, 40 * 26); 37 | for (size_t x = 0; x < 30; x++) { 38 | for (size_t c = 0; c < 13; c++) { 39 | scene[x * 15 + c + 2] = words[8 + x * 17 + c]; 40 | } 41 | } 42 | 43 | removefill(amsua2, words, 40 * 14); 44 | for (size_t x = 0; x < 30; x++) { 45 | for (size_t c = 0; c < 2; c++) { 46 | scene[x * 15 + c] = words[6 + x * 4 + c]; 47 | } 48 | } 49 | 50 | images[Imager::AMSUA]->push16Bit(scene, 0); 51 | } 52 | 53 | /// Removes fill words from AMSU frames 54 | void removefill(const uint8_t *in, uint16_t *out, size_t n) { 55 | size_t j = 0; 56 | for (size_t i = 0; i < n; i++) { 57 | uint16_t word = in[i * 2] << 8 | in[i * 2 + 1]; 58 | 59 | if (word != AMSU_FILL_WORD) { 60 | out[j++] = word; 61 | } 62 | } 63 | } 64 | 65 | private: 66 | }; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/decoders/common/tip.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | #include "image/raw.h" 23 | #include "protocol/repack.h" 24 | #include "satinfo.h" 25 | 26 | #ifndef LEANHRPT_DECODERS_COMMON_TIP_H_ 27 | #define LEANHRPT_DECODERS_COMMON_TIP_H_ 28 | 29 | /** 30 | * Check parity of a TIP frame 31 | * 32 | * @return If the parity was correct 33 | */ 34 | inline bool tip_parity(const uint8_t *frame) { 35 | bool ok = true; 36 | 37 | for (size_t i = 0; i < 6; i++) { 38 | size_t parity = 0; 39 | for (size_t j = 0; j < 17; j++) { 40 | parity += std::bitset<8>(frame[2 + i * 17 + j]).count(); 41 | } 42 | 43 | // Remove last bit on block 5 44 | if (i == 5) { 45 | parity -= std::bitset<8>(frame[103]).test(0); 46 | } 47 | 48 | if ((parity % 2) != std::bitset<8>(frame[103]).test(5 - i)) { 49 | ok = false; 50 | } 51 | } 52 | 53 | return ok; 54 | } 55 | 56 | class TIPDecoder { 57 | public: 58 | /** 59 | * Decode a HIRS Line 60 | * 61 | * @return If a line was decoded 62 | */ 63 | bool hirs_work(std::map &images, const uint8_t *frame) { 64 | // These are taken from the NOAA KLM Users Guide 65 | const size_t offsets[36] = {16, 17, 22, 23, 26, 27, 30, 31, 34, 35, 38, 39, 42, 43, 54, 55, 58, 59, 66 | 62, 63, 66, 67, 70, 71, 74, 75, 78, 79, 82, 83, 84, 85, 88, 89, 92, 93}; 67 | const size_t channels[20] = {1, 17, 2, 3, 13, 4, 18, 11, 19, 7, 8, 20, 10, 14, 6, 5, 15, 12, 16, 9}; 68 | 69 | // Extract HIRS/4 data 70 | uint8_t packet[36]; 71 | for (size_t i = 0; i < 36; i++) { 72 | packet[i] = frame[offsets[i]]; 73 | } 74 | 75 | // Repack into 13 bit words 76 | uint16_t words[22]; 77 | arbitrary_repack(packet, words, 22); 78 | 79 | uint8_t element_number = (words[1] >> 1) & 0b111111; 80 | if (element_number > 55) return false; 81 | 82 | for (size_t j = 0; j < 20; j++) { 83 | unsigned short *channel = images[Imager::HIRS]->getChannel(channels[j] - 1); 84 | 85 | bool sign_bit = words[j + 2] >> 12; 86 | int16_t val = words[j + 2] & 0b111111111111; 87 | val = sign_bit ? val : -val; 88 | 89 | channel[images[Imager::HIRS]->rows() * images[Imager::HIRS]->width() + element_number] = val * 8 + 32768; 90 | } 91 | 92 | // New line 93 | if (element_number == 55) { 94 | images[Imager::HIRS]->set_height(images[Imager::HIRS]->rows() + 1); 95 | return true; 96 | } 97 | return false; 98 | } 99 | 100 | private: 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /src/decoders/decoder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "decoder.h" 20 | 21 | #include "fengyun_hrpt.h" 22 | #include "meteor_hrpt.h" 23 | #include "meteor_lrpt.h" 24 | #include "metop_hrpt.h" 25 | #include "noaa_dsb.h" 26 | #include "noaa_gac.h" 27 | #include "noaa_hrpt.h" 28 | 29 | Decoder *Decoder::make(Protocol protocol, SatID sat) { 30 | Decoder *decoder; 31 | switch (protocol) { 32 | case Protocol::LRPT: 33 | decoder = new MeteorLRPTDecoder; 34 | break; 35 | case Protocol::HRPT: 36 | decoder = new NOAAHRPTDecoder; 37 | break; 38 | case Protocol::AHRPT: 39 | decoder = new MetopHRPTDecoder; 40 | break; 41 | case Protocol::MeteorHRPT: 42 | decoder = new MeteorHRPTDecoder; 43 | break; 44 | case Protocol::FengYunHRPT: 45 | decoder = new FengyunHRPTDecoder(sat); 46 | break; 47 | case Protocol::GAC: 48 | decoder = new NOAAGACDecoder(false); 49 | break; 50 | case Protocol::GACReverse: 51 | decoder = new NOAAGACDecoder(true); 52 | break; 53 | case Protocol::DSB: 54 | decoder = new NOAADSBDecoder; 55 | break; 56 | default: 57 | throw std::runtime_error("invalid value in enum `Protocol`"); 58 | } 59 | return decoder; 60 | } 61 | -------------------------------------------------------------------------------- /src/decoders/decoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_DECODER_H_ 20 | #define LEANHRPT_DECODERS_DECODER_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "image/raw.h" 29 | #include "satinfo.h" 30 | 31 | #define BUFFER_SIZE 1024 32 | 33 | struct Data { 34 | std::map imagers; 35 | std::map> timestamps; 36 | std::map caldata; 37 | std::vector ch3a; 38 | }; 39 | 40 | enum class FileType { Raw, CADU, VCDU, raw16, HRP, TIP, Unknown }; 41 | 42 | class Decoder { 43 | public: 44 | Decoder() : is_running(true) { buffer = new uint8_t[BUFFER_SIZE]; } 45 | virtual ~Decoder() { 46 | for (auto image : images) { 47 | delete image.second; 48 | } 49 | delete[] buffer; 50 | } 51 | 52 | /// Decode a file 53 | bool decodeFile(std::string filename, FileType filetype) { 54 | d_filetype = filetype; 55 | std::filebuf file; 56 | if (!file.open(filename, std::ios::in | std::ios::binary)) { 57 | return false; 58 | } 59 | std::istream stream(&file); 60 | get_filesize(stream); 61 | QDateTime _created = QFileInfo(QString::fromStdString(filename)).birthTime().toUTC(); 62 | if (!_created.isValid()) _created = QFileInfo(QString::fromStdString(filename)).lastModified().toUTC(); 63 | if (!_created.isValid()) _created = QDateTime::currentDateTimeUtc(); 64 | created = _created.toSecsSinceEpoch(); 65 | 66 | while (is_running && !stream.eof()) { 67 | work(stream); 68 | read = stream.tellg(); 69 | } 70 | 71 | file.close(); 72 | return true; 73 | } 74 | /// Current decode progress (0 to 1) 75 | float progress() { return static_cast(read) / static_cast(filesize); } 76 | 77 | /** 78 | * Stop the current decode 79 | * 80 | * This is only used when the decoder is running on another thread 81 | */ 82 | void stop() { is_running = false; } 83 | 84 | /// Get the produced data 85 | Data get() { return {images, timestamps, caldata, ch3a}; } 86 | 87 | /// Automatically make/allocate a decoder given satid 88 | static Decoder *make(Protocol protocol, SatID sat); 89 | 90 | protected: 91 | uint8_t *buffer; 92 | std::map images; 93 | std::map> timestamps; 94 | std::map caldata; 95 | std::vector ch3a; 96 | virtual void work(std::istream &stream) = 0; 97 | FileType d_filetype; 98 | time_t created; 99 | 100 | private: 101 | std::atomic is_running; 102 | size_t read = 0; 103 | size_t filesize = 1; 104 | 105 | void get_filesize(std::istream &stream) { 106 | // Get filesize 107 | stream.seekg(0, std::ios::end); 108 | filesize = stream.tellg(); 109 | 110 | // Seek back to the beginning 111 | stream.seekg(stream.beg); 112 | } 113 | }; 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /src/decoders/fengyun_hrpt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "fengyun_hrpt.h" 20 | 21 | void FengyunHRPTDecoder::work(std::istream &stream) { 22 | if (d_filetype == FileType::CADU) { 23 | stream.read(reinterpret_cast(frame), 1024); 24 | frame_work(frame); 25 | } else if (d_filetype == FileType::VCDU) { 26 | stream.read((char *)&frame[4], 892); 27 | frame_work(frame); 28 | } 29 | } 30 | 31 | void FengyunHRPTDecoder::frame_work(uint8_t *ptr) { 32 | uint8_t VCID = ptr[5] & 0b111111; 33 | if (VCID == 5) { 34 | if (virrDeframer.work(&ptr[14], line, 882)) { 35 | // Stolen from the power of @Aang254's SatDump 36 | uint8_t timestamp[8]; 37 | timestamp[0] = (line[26041] & 0b111111) << 2 | line[26042] >> 6; 38 | timestamp[1] = (line[26042] & 0b111111) << 2 | line[26043] >> 6; 39 | timestamp[2] = (line[26043] & 0b111111) << 2 | line[26044] >> 6; 40 | timestamp[3] = (line[26044] & 0b111111) << 2 | line[26045] >> 6; 41 | timestamp[4] = (line[26045] & 0b111111) << 2 | line[26046] >> 6; 42 | timestamp[6] = (line[26046] & 0b111111) << 2 | line[26047] >> 6; 43 | timestamp[7] = (line[26047] & 0b111111) << 2 | line[26048] >> 6; 44 | 45 | uint16_t days = (timestamp[1] & 0b11) << 10 | timestamp[2] << 2 | timestamp[3] >> 6; 46 | uint32_t ms = (timestamp[3] & 0b11) << 24 | timestamp[4] << 16 | timestamp[6] << 8 | timestamp[7]; 47 | timestamps[Imager::VIRR].push_back(launch_timestamp + days * 86400.0 + ms / 1000.0 + 12.0 * 3600.0); 48 | 49 | images[Imager::VIRR]->push10Bit(line, 349); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/decoders/fengyun_hrpt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_FENGYUN_HRPT_H_ 20 | #define LEANHRPT_DECODERS_FENGYUN_HRPT_H_ 21 | 22 | #include 23 | 24 | #include "decoder.h" 25 | #include "protocol/deframer.h" 26 | 27 | class FengyunHRPTDecoder : public Decoder { 28 | public: 29 | FengyunHRPTDecoder(SatID sat) : virrDeframer(8, false) { 30 | frame = new uint8_t[1024]; 31 | line = new uint8_t[208400 / 8]; 32 | images[Imager::VIRR] = new RawImage(2048, 10); 33 | 34 | switch (sat) { 35 | case SatID::FengYun3A: 36 | launch_timestamp = 1289347200.0; 37 | break; 38 | case SatID::FengYun3B: 39 | launch_timestamp = 1289347200.0; 40 | break; 41 | case SatID::FengYun3C: 42 | launch_timestamp = 1522368000.0; 43 | break; 44 | default: 45 | throw std::runtime_error("Non FY SatID passed to FengyunHRPTDecoder"); 46 | } 47 | } 48 | ~FengyunHRPTDecoder() { 49 | delete[] frame; 50 | delete[] line; 51 | } 52 | 53 | private: 54 | uint8_t *frame, *line; 55 | ArbitraryDeframer virrDeframer; 56 | double launch_timestamp; 57 | 58 | void work(std::istream &stream); 59 | void frame_work(uint8_t *ptr); 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/decoders/meteor_hrpt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "meteor_hrpt.h" 20 | 21 | #include 22 | #include 23 | 24 | #include "protocol/repack.h" 25 | 26 | void MeteorHRPTDecoder::work(std::istream &stream) { 27 | if (d_filetype == FileType::CADU) { 28 | stream.read(reinterpret_cast(frame), 1024); 29 | frame_work(frame); 30 | } else { 31 | stream.read(reinterpret_cast(buffer), BUFFER_SIZE); 32 | if (deframer.work(buffer, frame, BUFFER_SIZE)) { 33 | frame_work(frame); 34 | } 35 | } 36 | } 37 | 38 | void MeteorHRPTDecoder::frame_work(uint8_t *ptr) { 39 | // See Table 1 - Structure of a transport frame 40 | std::memcpy(&msumrBuffer[238 * 0], &ptr[23 - 1], 238); 41 | std::memcpy(&msumrBuffer[238 * 1], &ptr[279 - 1], 238); 42 | std::memcpy(&msumrBuffer[238 * 2], &ptr[535 - 1], 238); 43 | std::memcpy(&msumrBuffer[238 * 3], &ptr[791 - 1], 234); 44 | 45 | if (MSUMRDeframer.work(msumrBuffer, msumrFrame, 948)) { 46 | int hours = msumrFrame[8]; 47 | hours = (hours - 3) % 24; // Moscow to UTC 48 | int minutes = msumrFrame[9]; 49 | int seconds = msumrFrame[10]; 50 | int delay = msumrFrame[10]; 51 | 52 | if (hours < 24 && minutes < 60 && seconds < 60) { 53 | time_t day = created / 86400 * 86400; 54 | msumr_timestamp = 55 | (double)day + (double)hours * 3600.0 + (double)minutes * 60.0 + (double)seconds + (double)delay / 255.0; 56 | } else { 57 | msumr_timestamp = 0.0; 58 | } 59 | timestamps[Imager::MSUMR].push_back(msumr_timestamp); 60 | 61 | images[Imager::MSUMR]->push10Bit(&msumrFrame[50], 0); 62 | 63 | uint16_t out[12]; 64 | repack10(&msumrFrame[35], out, 12); 65 | caldata["wl1_sum"] += out[0]; 66 | caldata["bl1_sum"] += out[1]; 67 | caldata["wl2_sum"] += out[2]; 68 | caldata["bl2_sum"] += out[3]; 69 | caldata["wl3_sum"] += out[4]; 70 | caldata["bl3_sum"] += out[5]; 71 | caldata["ch4_space"] += out[6]; 72 | caldata["ch4_cal"] += out[7]; 73 | caldata["ch5_space"] += out[8]; 74 | caldata["ch5_cal"] += out[9]; 75 | caldata["ch6_space"] += out[10]; 76 | caldata["ch6_cal"] += out[11]; 77 | caldata["blackbody_temperature_sum"] += 300; 78 | caldata["n"] += 1.0; 79 | } 80 | 81 | uint8_t mtvza_buffer[32]; 82 | uint8_t mtvza_frame[248]; 83 | std::memcpy(&mtvza_buffer[8 * 0], &ptr[15 - 1], 8); 84 | std::memcpy(&mtvza_buffer[8 * 1], &ptr[271 - 1], 8); 85 | std::memcpy(&mtvza_buffer[8 * 2], &ptr[527 - 1], 8); 86 | std::memcpy(&mtvza_buffer[8 * 3], &ptr[783 - 1], 8); 87 | 88 | if (mtvza_deframer.work(mtvza_buffer, mtvza_frame, 32)) { 89 | // Frame type, only type 255 contains imagery 90 | if (mtvza_frame[4] != 255) return; 91 | 92 | // X position 93 | uint8_t x = mtvza_frame[5] - 2; 94 | if (x > 24) return; 95 | 96 | mtvza_work(x, 0, &mtvza_frame[8]); 97 | mtvza_work(x, 4, &mtvza_frame[128]); 98 | 99 | // End of a line 100 | if (x == 24) { 101 | images[Imager::MTVZA]->set_height(images[Imager::MTVZA]->rows() + 1); 102 | timestamps[Imager::MTVZA].push_back(msumr_timestamp); 103 | } 104 | } 105 | } 106 | 107 | void MeteorHRPTDecoder::mtvza_work(uint8_t x, size_t offset, uint8_t *ptr) { 108 | const size_t channel_length[30] = {1, 1, 1, 1, 1, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; 109 | const size_t channel_offset[30] = {0, 1, 2, 3, 4, 5, 9, 13, 15, 17, 19, 21, 23, 25, 27, 110 | 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57}; 111 | 112 | for (size_t i = 0; i < 30; i++) { 113 | unsigned short *channel = images[Imager::MTVZA]->getChannel(i); 114 | 115 | for (size_t j = 0; j < 4; j++) { 116 | size_t ch = channel_offset[i]; 117 | if (channel_length[i] == 4) { 118 | ch += j; 119 | } else if (channel_length[i] == 2) { 120 | ch += j / 2; 121 | } 122 | 123 | int16_t val = ptr[ch * 2 + 1] << 8 | ptr[ch * 2]; 124 | channel[images[Imager::MTVZA]->rows() * images[Imager::MTVZA]->width() + x * 8 + j + offset] = val + 32768; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/decoders/meteor_hrpt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_METEOR_HRPT_H_ 20 | #define LEANHRPT_DECODERS_METEOR_HRPT_H_ 21 | 22 | #include 23 | 24 | #include "decoder.h" 25 | #include "protocol/ccsds/deframer.h" 26 | #include "protocol/deframer.h" 27 | 28 | // http://planet.iitp.ru/retro/index.php?lang=en&page_type=spacecraft&page=meteor_m_n2_structure_1 29 | class MeteorHRPTDecoder : public Decoder { 30 | public: 31 | MeteorHRPTDecoder() : MSUMRDeframer(9, false), mtvza_deframer(4, false) { 32 | frame = new uint8_t[1024]; 33 | msumrBuffer = new uint8_t[948]; 34 | msumrFrame = new uint8_t[11850]; 35 | images[Imager::MSUMR] = new RawImage(1572, 6, 4); 36 | images[Imager::MTVZA] = new RawImage(200, 30); 37 | } 38 | ~MeteorHRPTDecoder() { 39 | delete[] frame; 40 | delete[] msumrBuffer; 41 | delete[] msumrFrame; 42 | } 43 | 44 | private: 45 | uint8_t *frame, *msumrBuffer, *msumrFrame; 46 | ccsds::Deframer deframer; 47 | ArbitraryDeframer MSUMRDeframer; 48 | ArbitraryDeframer mtvza_deframer; 49 | double msumr_timestamp = 0.0; 50 | 51 | void work(std::istream &stream); 52 | void frame_work(uint8_t *ptr); 53 | void mtvza_work(uint8_t x, size_t offset, uint8_t *ptr); 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/decoders/meteor_lrpt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "meteor_lrpt.h" 20 | 21 | #include 22 | #include 23 | 24 | #include "protocol/lrpt/packet.h" 25 | #include "protocol/repack.h" 26 | 27 | void MeteorLRPTDecoder::work(std::istream &stream) { 28 | if (d_filetype == FileType::CADU) { 29 | stream.read(reinterpret_cast(frame), 1024); 30 | frame_work(); 31 | } else if (d_filetype == FileType::VCDU) { 32 | stream.read((char *)&frame[4], 892); 33 | frame_work(); 34 | } 35 | } 36 | 37 | void MeteorLRPTDecoder::frame_work() { 38 | uint8_t VCID = frame[5] & 0b111111; 39 | if (VCID != 5) return; 40 | 41 | auto packets = demux.work(frame); 42 | for (const std::vector &packet : packets) { 43 | ccsds::CPPDUHeader header(packet); 44 | if (start_offset == 0) { 45 | if (header.apid == 70) { 46 | start_offset = header.counter + 1; 47 | } 48 | continue; 49 | } 50 | if (header.apid == 70 && header.length == 58) { 51 | const uint8_t *data = &packet[14]; 52 | 53 | uint16_t out[12]; 54 | repack10(&data[35], out, 12); 55 | caldata["wl1_sum"] += out[0]; 56 | caldata["bl1_sum"] += out[1]; 57 | caldata["wl2_sum"] += out[2]; 58 | caldata["bl2_sum"] += out[3]; 59 | caldata["wl3_sum"] += out[4]; 60 | caldata["bl3_sum"] += out[5]; 61 | caldata["ch4_space"] += out[6]; 62 | caldata["ch4_cal"] += out[7]; 63 | caldata["ch5_space"] += out[8]; 64 | caldata["ch5_cal"] += out[9]; 65 | caldata["ch6_space"] += out[10]; 66 | caldata["ch6_cal"] += out[11]; 67 | caldata["blackbody_temperature_sum"] += 300; 68 | caldata["n"] += 1.0; 69 | } 70 | if (header.apid < 64 || header.apid > 69) continue; 71 | 72 | // Sanity check 73 | uint8_t seq = packet[6 + 8]; 74 | if (seq > 196) continue; 75 | 76 | // Current day (always zero) 77 | // uint16_t day = packet[6+0] << 8 | packet[6+1]; 78 | // Millisecond of day 79 | uint32_t timestamp = packet[6 + 2] << 24 | packet[6 + 3] << 16 | packet[6 + 4] << 8 | packet[6 + 5]; 80 | 81 | // JPEG decoding 82 | std::array, MCU_PER_PACKET> pixels; 83 | uint8_t q = packet[6 + 13]; 84 | if (!lrpt::decode_packet(pixels, &packet[6 + 14], q, header.length - 6)) continue; 85 | 86 | // Handle counter overflow 87 | if (last_counter > (size_t)header.counter + 8192) { 88 | counter_offset += 16384; // 2^14 89 | } 90 | 91 | time_t day = created / 86400 * 86400; 92 | 93 | size_t counter = header.counter + counter_offset - start_offset; 94 | for (size_t j = 0; j < MCU_PER_PACKET; j++) { 95 | for (size_t y = 0; y < 8; y++) { 96 | for (size_t x = 0; x < 8; x++) { 97 | size_t x1 = x + (j + seq) * 8; 98 | size_t y1 = counter / (14 * 3 + 1) * 8 + y; 99 | 100 | if (y == 0) { 101 | timestamps[Imager::MSUMR].resize(y1 + 8); 102 | } 103 | timestamps[Imager::MSUMR][y1] = (double)day + (double)timestamp / 1000.0 - 10800.0 + y * 0.205; 104 | 105 | images[Imager::MSUMR]->set_height(y1 + 1); 106 | unsigned short *ch = images[Imager::MSUMR]->getChannel(header.apid - 64); 107 | 108 | ch[y1 * 1568 + x1] = pixels[j][y][x] * 256; 109 | } 110 | } 111 | } 112 | 113 | last_counter = header.counter; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/decoders/meteor_lrpt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_METEOR_LRPT_H_ 20 | #define LEANHRPT_DECODERS_METEOR_LRPT_H_ 21 | 22 | #include "decoder.h" 23 | #include "protocol/ccsds/demuxer.h" 24 | 25 | // http://planet.iitp.ru/retro/index.php?lang=en&page_type=spacecraft&page=meteor_m_n2_structure_2 26 | class MeteorLRPTDecoder : public Decoder { 27 | public: 28 | MeteorLRPTDecoder() { 29 | // Allocate all 6 channels for correct channel labeling 30 | images[Imager::MSUMR] = new RawImage(1568, 6); 31 | timestamps[Imager::MSUMR].reserve(10000); 32 | } 33 | 34 | private: 35 | uint8_t frame[1024]; 36 | ccsds::Demuxer demux; 37 | 38 | size_t counter_offset = 0; 39 | size_t start_offset = 0; 40 | size_t last_counter = 0; 41 | 42 | void work(std::istream &stream); 43 | void frame_work(); 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/decoders/metop_hrpt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "metop_hrpt.h" 20 | 21 | #include "protocol/repack.h" 22 | 23 | void MetopHRPTDecoder::work(std::istream &stream) { 24 | if (d_filetype == FileType::CADU) { 25 | stream.read(reinterpret_cast(frame), 1024); 26 | frame_work(frame); 27 | } else if (d_filetype == FileType::VCDU) { 28 | stream.read((char *)&frame[4], 892); 29 | frame_work(frame); 30 | } 31 | } 32 | 33 | void MetopHRPTDecoder::frame_work(uint8_t *ptr) { 34 | uint8_t VCID = ptr[5] & 0b111111; 35 | if (VCID == 9) { 36 | std::vector line = demux.work(ptr); 37 | 38 | // The only thing that VCID 9 will ever contain is AVHRR data so no need for APID filtering 39 | if (line.size() == 12966) { 40 | uint16_t data[10355]; 41 | repack10(&line[20], data, 10355); 42 | images[Imager::AVHRR]->push16Bit(&data[11 * 5], 0, 64); 43 | 44 | // Days since 01/01/2000 45 | uint16_t days = line[6] << 8 | line[7]; 46 | // Milliseconds since start of the day 47 | uint32_t ms = line[8] << 24 | line[9] << 16 | line[10] << 8 | line[11]; 48 | 49 | double timestamp = 946684800.0 + days * 86400.0 + ms / 1000.0; 50 | timestamps[Imager::AVHRR].push_back(timestamp); 51 | ch3a.push_back(ccsds::CPPDUHeader(line).apid == 103); 52 | 53 | // Space view 54 | for (size_t i = 0; i < 5; i++) { 55 | double sum = 0.0; 56 | for (size_t x = 0; x < 10; x++) { 57 | sum += data[x * 5 + i]; 58 | } 59 | 60 | caldata["ch" + std::to_string(i + 1) + "_space"] += sum / 10.0; 61 | } 62 | 63 | // PRTs 64 | uint16_t *prt = &data[10295 + 2]; 65 | if (prt[0] != 0) { 66 | double sum = 0.0; 67 | for (size_t i = 0; i < 3; i++) { 68 | // TODO: calibrate each PRT separately 69 | // Currently this uses the average of all 4 coefficients (excluding d2) 70 | sum += 276.57465 + prt[i] * 0.050912; 71 | } 72 | 73 | blackbody_temperature = sum / 3.0; 74 | } 75 | caldata["blackbody_temperature_sum"] += blackbody_temperature; 76 | 77 | // Back Scan 78 | for (size_t i = 0; i < 5; i++) { 79 | double sum = 0.0; 80 | for (size_t x = 0; x < 10; x++) { 81 | sum += data[10305 + x * 5 + i]; 82 | } 83 | 84 | caldata["ch" + std::to_string(i + 1) + "_cal"] += sum / 10.0; 85 | } 86 | caldata["n"] += 1.0; 87 | } 88 | } else if (VCID == 12) { 89 | std::vector line = mhs_demux.work(ptr); 90 | 91 | if (line.size() == 1308) { 92 | images[Imager::MHS]->push16Bit((uint16_t *)&line[70], 0); 93 | 94 | // Days since 01/01/2000 95 | uint16_t days = line[6] << 8 | line[7]; 96 | // Milliseconds since start of the day 97 | uint32_t ms = line[8] << 24 | line[9] << 16 | line[10] << 8 | line[11]; 98 | 99 | double timestamp = 946684800.0 + days * 86400.0 + ms / 1000.0; 100 | timestamps[Imager::MHS].push_back(timestamp); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/decoders/metop_hrpt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_METOP_HRPT_H_ 20 | #define LEANHRPT_DECODERS_METOP_HRPT_H_ 21 | 22 | #include 23 | 24 | #include "decoder.h" 25 | #include "protocol/ccsds/demuxer.h" 26 | 27 | class MetopHRPTDecoder : public Decoder { 28 | public: 29 | MetopHRPTDecoder() { 30 | images[Imager::AVHRR] = new RawImage(2048, 5); 31 | images[Imager::MHS] = new RawImage(90, 6); 32 | frame = new uint8_t[1024]; 33 | } 34 | ~MetopHRPTDecoder() { delete[] frame; } 35 | 36 | private: 37 | uint8_t *frame; 38 | ccsds::SimpleDemuxer demux, mhs_demux; 39 | double blackbody_temperature = 290; 40 | 41 | void work(std::istream &stream); 42 | void frame_work(uint8_t *ptr); 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/decoders/noaa_dsb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_NOAA_DSB_H_ 20 | #define LEANHRPT_DECODERS_NOAA_DSB_H_ 21 | 22 | #include "common/tip.h" 23 | #include "decoder.h" 24 | 25 | class NOAADSBDecoder : public Decoder { 26 | public: 27 | NOAADSBDecoder() { images[Imager::HIRS] = new RawImage(56, 20); } 28 | 29 | private: 30 | TIPDecoder tip_decoder; 31 | 32 | void work(std::istream &stream) { 33 | uint8_t frame[104]; 34 | if (d_filetype == FileType::TIP) { 35 | stream.read((char *)frame, 104); 36 | tip_decoder.hirs_work(images, frame); 37 | } 38 | } 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/decoders/noaa_gac.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "noaa_gac.h" 20 | 21 | #include 22 | 23 | #include "protocol/reverse.h" 24 | 25 | // Contains 1023 bits of data, last bit is zero 26 | uint8_t pattern[128] = {0x07, 0x40, 0xc9, 0x29, 0x42, 0x5e, 0xee, 0xa8, 0xee, 0x2c, 0x2f, 0xb1, 0xf5, 0x24, 0x21, 0xc8, 27 | 0xe6, 0x60, 0x36, 0x6c, 0x5c, 0x79, 0x6f, 0x04, 0x9c, 0xed, 0x36, 0xaa, 0xfd, 0x2a, 0x58, 0xdb, 28 | 0xa2, 0x77, 0x92, 0xd6, 0x45, 0x20, 0x07, 0xc4, 0x08, 0xb4, 0x98, 0xcb, 0x3a, 0x44, 0x29, 0x84, 29 | 0xff, 0xbd, 0x9f, 0x31, 0x12, 0xb5, 0x15, 0x89, 0x9c, 0x2b, 0x97, 0xf9, 0xca, 0xf5, 0x66, 0x41, 30 | 0x06, 0x0b, 0x2a, 0xdc, 0x1a, 0x3f, 0xad, 0x07, 0x02, 0xa9, 0xe7, 0xaf, 0x14, 0x04, 0xde, 0x8d, 31 | 0xf8, 0x47, 0xb7, 0xc0, 0x2e, 0xb8, 0x76, 0x1f, 0x94, 0xe3, 0x4f, 0xb9, 0xb9, 0x3d, 0xfc, 0x61, 32 | 0xbb, 0x2e, 0xfa, 0x16, 0xd1, 0x79, 0xa9, 0xa5, 0xcf, 0xda, 0xe9, 0x94, 0x67, 0x8e, 0x24, 0x63, 33 | 0xa8, 0x28, 0x8d, 0x7c, 0x86, 0x2a, 0x1a, 0xbb, 0x6c, 0x9a, 0xd8, 0x3c, 0x33, 0x43, 0xd3, 0xac}; 34 | 35 | void NOAAGACDecoder::init_xor() { 36 | uint8_t buf = 0; 37 | size_t offset = 60; 38 | size_t bit = offset % 8; 39 | size_t byte = offset / 8; 40 | 41 | for (size_t i = 0; i < 33270 - offset; i++) { 42 | size_t j = i % 1023; 43 | buf = buf << 1 | std::bitset<8>(pattern[j / 8]).test(7 - (j % 8)); 44 | bit++; 45 | 46 | if (bit == 8) { 47 | xor_table[byte++] = buf; 48 | bit = 0; 49 | } 50 | } 51 | } 52 | 53 | void NOAAGACDecoder::work(std::istream &stream) { 54 | if (d_filetype == FileType::Raw) { 55 | stream.read(reinterpret_cast(buffer), BUFFER_SIZE); 56 | 57 | if (d_reverse) { 58 | if (deframer_reverse.work(buffer, frame, BUFFER_SIZE)) { 59 | for (size_t i = 0; i < 4159; i++) { 60 | frame[i] = reverse_bits(frame[i]); 61 | } 62 | for (size_t i = 0; i < 4159 / 2; i++) { 63 | std::swap(frame[i], frame[4158 - i]); 64 | } 65 | for (size_t i = 0; i < 4159; i++) { 66 | frame[i] ^= xor_table[i]; 67 | } 68 | 69 | repack10(frame, repacked, 3327 - 3); 70 | frame_work(repacked); 71 | } 72 | } else { 73 | if (deframer.work(buffer, frame, BUFFER_SIZE)) { 74 | for (size_t i = 0; i < 4159; i++) { 75 | frame[i] ^= xor_table[i]; 76 | } 77 | 78 | repack10(frame, repacked, 3327 - 3); 79 | frame_work(repacked); 80 | } 81 | } 82 | } 83 | } 84 | 85 | void NOAAGACDecoder::frame_work(uint16_t *ptr) { 86 | uint16_t *data = &ptr[103]; 87 | // bool line_ok = true; 88 | 89 | // Parse TIP/AIP frames 90 | for (size_t i = 0; i < 10; i++) { 91 | uint8_t frame[104]; 92 | // bool parity_ok = true; 93 | 94 | for (size_t j = 0; j < 104; j++) { 95 | uint16_t word = data[104 * i + j]; 96 | frame[j] = word >> 2; 97 | 98 | // Parity check 99 | /*bool parity = std::bitset<8>(frame[j]).count() % 2; 100 | bool expected_parity = std::bitset<16>(word).test(1); 101 | if (parity != expected_parity) { 102 | parity_ok = false; 103 | }*/ 104 | } 105 | 106 | /*if (!parity_ok) { 107 | line_ok = false; 108 | continue; 109 | }*/ 110 | 111 | if (i < 5) { 112 | if (tip_decoder.hirs_work(images, frame)) { 113 | timestamps[Imager::HIRS].push_back(timestamp); 114 | } 115 | } else { 116 | if (aip_decoder.work(images, frame)) { 117 | timestamps[Imager::MHS].push_back(0); 118 | timestamps[Imager::MHS].push_back(0); 119 | timestamps[Imager::MHS].push_back(timestamp); 120 | timestamps[Imager::AMSUA].push_back(timestamp); 121 | } 122 | } 123 | } 124 | 125 | // Extract calibration data 126 | uint16_t *prt = &ptr[17]; 127 | if (prt[0] != 0) { 128 | double sum = 0.0; 129 | for (size_t i = 0; i < 3; i++) { 130 | // TODO: calibrate each PRT separately 131 | // Currently this uses the average of all 4 coefficients (excluding d2) 132 | sum += 276.57465 + prt[i] * 0.050912; 133 | } 134 | 135 | blackbody_temperature = sum / 3.0; 136 | } 137 | caldata["blackbody_temperature_sum"] += blackbody_temperature; 138 | 139 | for (size_t i = 0; i < 5; i++) { 140 | double sum = 0.0; 141 | for (size_t x = 0; x < 10; x++) { 142 | sum += ptr[52 + x * 5 + i]; 143 | } 144 | 145 | caldata["ch" + std::to_string(i + 1) + "_space"] += sum / 10.0; 146 | } 147 | 148 | for (size_t i = 0; i < 3; i++) { 149 | double sum = 0.0; 150 | for (size_t x = 0; x < 10; x++) { 151 | sum += ptr[22 + x * 3 + i]; 152 | } 153 | 154 | caldata["ch" + std::to_string(i + 3) + "_cal"] += sum / 10.0; 155 | } 156 | caldata["n"] += 1.0; 157 | 158 | // Calculate the timestamp of the start of the year 159 | int _year = QDateTime::fromSecsSinceEpoch(created).date().year(); 160 | double year = QDate(_year, 1, 1).startOfDay(Qt::UTC).toSecsSinceEpoch() - 86400.0; 161 | 162 | // Parse timestamp 163 | uint16_t days = repacked[8] >> 1; 164 | uint32_t ms = (repacked[9] & 0b1111111) << 20 | repacked[10] << 10 | repacked[11]; 165 | timestamp = (double)year + (double)days * 86400.0 + (double)ms / 1000.0; 166 | timestamps[Imager::AVHRR].push_back(timestamp); 167 | 168 | ch3a.push_back(std::bitset<10>(ptr[6]).test(0)); 169 | 170 | for (size_t i = 0; i < 3327; i++) { 171 | ptr[i] *= 64; 172 | } 173 | images[Imager::AVHRR]->push16Bit(ptr, 1182); 174 | } 175 | -------------------------------------------------------------------------------- /src/decoders/noaa_gac.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_NOAA_GAC_H_ 20 | #define LEANHRPT_DECODERS_NOAA_GAC_H_ 21 | 22 | #include "common/aip.h" 23 | #include "common/tip.h" 24 | #include "decoder.h" 25 | #include "protocol/deframer.h" 26 | 27 | class NOAAGACDecoder : public Decoder { 28 | public: 29 | NOAAGACDecoder(bool reverse) : d_reverse(reverse), deframer(8, true), deframer_reverse(8, true) { 30 | images[Imager::AVHRR] = new RawImage(409, 5); 31 | images[Imager::HIRS] = new RawImage(56, 20); 32 | images[Imager::MHS] = new RawImage(90, 6); 33 | images[Imager::AMSUA] = new RawImage(30, 15); 34 | init_xor(); 35 | } 36 | 37 | private: 38 | const bool d_reverse; 39 | ArbitraryDeframer deframer; 40 | ArbitraryDeframer deframer_reverse; 41 | double timestamp = 0.0; 42 | double blackbody_temperature = 290; 43 | 44 | uint8_t frame[4159]; 45 | uint16_t repacked[3327]; 46 | 47 | uint8_t xor_table[4159]; 48 | void init_xor(); 49 | 50 | void work(std::istream &stream); 51 | void frame_work(uint16_t *ptr); 52 | 53 | AIPDecoder aip_decoder; 54 | TIPDecoder tip_decoder; 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/decoders/noaa_hrpt.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "noaa_hrpt.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "protocol/repack.h" 29 | 30 | void NOAAHRPTDecoder::work(std::istream &stream) { 31 | if (d_filetype == FileType::raw16) { 32 | stream.read(reinterpret_cast(repacked), 11090 * 2); 33 | frame_work(repacked); 34 | } else if (d_filetype == FileType::HRP) { 35 | stream.read(reinterpret_cast(repacked), 11090 * 2); 36 | for (size_t i = 0; i < 11090; i++) { 37 | uint16_t x = repacked[i]; 38 | repacked[i] = (x << 8) | (x >> 8); 39 | } 40 | frame_work(repacked); 41 | } else if (d_filetype == FileType::Raw) { 42 | stream.read(reinterpret_cast(buffer), BUFFER_SIZE); 43 | if (deframer.work(buffer, frame, BUFFER_SIZE)) { 44 | repack10(frame, repacked, 11090 - 3); 45 | frame_work(repacked); 46 | } 47 | } 48 | } 49 | 50 | void NOAAHRPTDecoder::frame_work(uint16_t *ptr) { 51 | uint16_t *data = &ptr[103]; 52 | // bool line_ok = true; 53 | 54 | // Parse TIP/AIP frames 55 | for (size_t i = 0; i < 5; i++) { 56 | uint8_t frame[104]; 57 | bool parity_ok = true; 58 | 59 | for (size_t j = 0; j < 104; j++) { 60 | uint16_t word = data[104 * i + j]; 61 | frame[j] = word >> 2; 62 | 63 | // Parity check 64 | bool parity = std::bitset<8>(frame[j]).count() % 2; 65 | bool expected_parity = std::bitset<16>(word).test(1); 66 | if (parity != expected_parity) { 67 | parity_ok = false; 68 | } 69 | } 70 | 71 | if (!parity_ok) { 72 | // line_ok = false; 73 | continue; 74 | } 75 | 76 | uint8_t frame_type = (ptr[6] >> 7) & 0b11; 77 | switch (frame_type) { 78 | case 1: { 79 | if (tip_decoder.hirs_work(images, frame)) { 80 | timestamps[Imager::HIRS].push_back(timestamp); 81 | } 82 | break; 83 | } 84 | case 3: { 85 | if (aip_decoder.work(images, frame)) { 86 | timestamps[Imager::MHS].push_back(0); 87 | timestamps[Imager::MHS].push_back(0); 88 | timestamps[Imager::MHS].push_back(timestamp); 89 | timestamps[Imager::AMSUA].push_back(timestamp); 90 | }; 91 | break; 92 | } 93 | default: 94 | break; 95 | } 96 | } 97 | 98 | // Extract calibration data 99 | uint16_t *prt = &ptr[17]; 100 | if (prt[0] != 0) { 101 | double sum = 0.0; 102 | for (size_t i = 0; i < 3; i++) { 103 | // TODO: calibrate each PRT separately 104 | // Currently this uses the average of all 4 coefficients (excluding d2) 105 | sum += 276.57465 + prt[i] * 0.050912; 106 | } 107 | 108 | blackbody_temperature = sum / 3.0; 109 | } 110 | caldata["blackbody_temperature_sum"] += blackbody_temperature; 111 | 112 | for (size_t i = 0; i < 5; i++) { 113 | double sum = 0.0; 114 | for (size_t x = 0; x < 10; x++) { 115 | sum += ptr[52 + x * 5 + i]; 116 | } 117 | 118 | caldata["ch" + std::to_string(i + 1) + "_space"] += sum / 10.0; 119 | } 120 | 121 | for (size_t i = 0; i < 3; i++) { 122 | double sum = 0.0; 123 | for (size_t x = 0; x < 10; x++) { 124 | sum += ptr[22 + x * 3 + i]; 125 | } 126 | 127 | caldata["ch" + std::to_string(i + 3) + "_cal"] += sum / 10.0; 128 | } 129 | caldata["n"] += 1.0; 130 | 131 | // Calculate the timestamp of the start of the year 132 | int _year = QDateTime::fromSecsSinceEpoch(created).date().year(); 133 | double year = QDate(_year, 1, 1).startOfDay(Qt::UTC).toSecsSinceEpoch() - 86400.0; 134 | 135 | // Parse timestamp 136 | uint16_t days = repacked[8] >> 1; 137 | uint32_t ms = (repacked[9] & 0b1111111) << 20 | repacked[10] << 10 | repacked[11]; 138 | timestamp = (double)year + (double)days * 86400.0 + (double)ms / 1000.0; 139 | timestamps[Imager::AVHRR].push_back(timestamp); 140 | 141 | ch3a.push_back(std::bitset<10>(ptr[6]).test(0)); 142 | 143 | for (size_t i = 0; i < 11090; i++) { 144 | ptr[i] *= 64; 145 | } 146 | images[Imager::AVHRR]->push16Bit(ptr, 750); 147 | } 148 | -------------------------------------------------------------------------------- /src/decoders/noaa_hrpt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_DECODERS_NOAA_HRPT_H_ 20 | #define LEANHRPT_DECODERS_NOAA_HRPT_H_ 21 | 22 | #include "common/aip.h" 23 | #include "common/tip.h" 24 | #include "decoder.h" 25 | #include "protocol/deframer.h" 26 | 27 | class NOAAHRPTDecoder : public Decoder { 28 | public: 29 | NOAAHRPTDecoder() : deframer(8, true) { 30 | frame = new uint8_t[(11090 * 10) / 8]; 31 | repacked = new uint16_t[11090]; 32 | images[Imager::AVHRR] = new RawImage(2048, 5); 33 | images[Imager::MHS] = new RawImage(90, 6); 34 | images[Imager::HIRS] = new RawImage(56, 20); 35 | images[Imager::AMSUA] = new RawImage(30, 15); 36 | } 37 | ~NOAAHRPTDecoder() { 38 | delete[] frame; 39 | delete[] repacked; 40 | } 41 | 42 | private: 43 | uint8_t *frame; 44 | uint16_t *repacked; 45 | ArbitraryDeframer deframer; 46 | double timestamp = 0.0; 47 | double blackbody_temperature = 290; 48 | 49 | void work(std::istream &stream); 50 | void frame_work(uint16_t *ptr); 51 | void cal_data(uint16_t *ptr); 52 | 53 | AIPDecoder aip_decoder; 54 | TIPDecoder tip_decoder; 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/fingerprint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_FINGERPRINT_H_ 20 | #define LEANHRPT_FINGERPRINT_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "decoders/decoder.h" 28 | #include "satinfo.h" 29 | 30 | enum class Suggestion { Automatic, POESHRPT, POESGAC, POESDSB, MeteorHRPT, MeteorLRPT, MetOpHRPT, FengYunHRPT }; 31 | 32 | class Fingerprint { 33 | public: 34 | Fingerprint() : is_running(true) {} 35 | 36 | /** 37 | * Start fingerprinting a file 38 | * 39 | * @returns The detected satellite, filetype and protocol 40 | */ 41 | std::tuple file(std::string filename, Suggestion suggestion); 42 | 43 | /** 44 | * Stop fingerprinting a file 45 | * 46 | * This is a only used when running in a thread 47 | */ 48 | void stop() { is_running = false; } 49 | 50 | private: 51 | SatID fingerprint_ccsds(std::istream &stream, FileType type); 52 | SatID fingerprint_noaa(std::istream &stream, FileType type); 53 | SatID fingerprint_meteor(std::istream &stream, FileType type); 54 | SatID fingerprint_gac(std::istream &stream, bool reverse); 55 | SatID fingerprint_dsb(std::istream &stream); 56 | 57 | Protocol fingerprint_raw(std::istream &stream, Suggestion suggestion); 58 | static std::set ccsds_downlinks(SatID id); 59 | 60 | static FileType id_magic(std::istream &stream) { 61 | uint8_t header[4]; 62 | stream.read((char *)&header, 4); 63 | stream.seekg(stream.beg); 64 | if (header[0] == 0x1A && header[1] == 0xCF && header[2] == 0xFC && header[3] == 0x1D) return FileType::CADU; 65 | if (header[0] == 0x84 && header[1] == 0x02 && header[2] && 0x6F && header[3] == 0x01) return FileType::raw16; 66 | if (header[0] == 0x02 && header[1] == 0x84 && header[2] && 0x01 && header[3] == 0x6F) return FileType::HRP; 67 | if (header[0] == 0xED && header[1] == 0xE2) return FileType::TIP; 68 | return FileType::Unknown; 69 | } 70 | 71 | std::atomic is_running; 72 | }; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/geo/crs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "crs.h" 20 | 21 | #include 22 | 23 | #include "util.h" 24 | 25 | static transform::XY forward_equirectangular(transform::Geo geo) { 26 | double lon = geo.x(); 27 | double lat = geo.y(); 28 | return transform::XY((lon / M_PI + 1.0) / 2.0, (-lat / M_PI_2 + 1.0) / 2.0); 29 | } 30 | static transform::XY forward_mercator(transform::Geo geo) { 31 | double lon = geo.x(); 32 | double lat = geo.y(); 33 | double y = -log(tan(M_PI_4 + lat / 2.0)) / M_PI; 34 | return transform::XY((lon / M_PI + 1.0) / 2.0, (y + 1.0) / 2.0); 35 | } 36 | static transform::XY forward_north_polar(transform::Geo geo) { 37 | double lon = geo.x(); 38 | double lat = geo.y(); 39 | double x = sin(lon) * (-lat + M_PI_2) / M_PI; 40 | double y = cos(lon) * (-lat + M_PI_2) / M_PI; 41 | return transform::XY((x + 1.0) / 2.0, (y + 1.0) / 2.0); 42 | } 43 | static transform::XY forward_south_polar(transform::Geo geo) { 44 | double lon = geo.x(); 45 | double lat = geo.y(); 46 | double x = sin(lon) * (lat + M_PI_2) / M_PI; 47 | double y = -cos(lon) * (lat + M_PI_2) / M_PI; 48 | return transform::XY((x + 1.0) / 2.0, (y + 1.0) / 2.0); 49 | } 50 | transform::XY transform::forward(transform::Geo geo, CRS crs) { 51 | switch (crs) { 52 | case CRS::Equirectangular: 53 | return forward_equirectangular(geo); 54 | case CRS::Mercator: 55 | return forward_mercator(geo); 56 | case CRS::North_Polar: 57 | return forward_north_polar(geo); 58 | case CRS::South_Polar: 59 | return forward_south_polar(geo); 60 | default: 61 | throw std::out_of_range("Unknown CRS"); 62 | } 63 | } 64 | 65 | static transform::Geo reverse_equirectangular(transform::XY xy) { 66 | double x = xy.x() * 2.0 - 1.0; 67 | double y = xy.y() * 2.0 - 1.0; 68 | return transform::Geo(x * M_PI, -y * M_PI_2); 69 | } 70 | static transform::Geo reverse_mercator(transform::XY xy) { 71 | double x = xy.x() * 2.0 - 1.0; 72 | double y = xy.y() * 2.0 - 1.0; 73 | double lat = 2.0 * atan(exp(-y * M_PI)) - M_PI_2; 74 | return transform::Geo(x * M_PI, lat); 75 | } 76 | static transform::Geo reverse_north_polar(transform::XY xy) { 77 | double x = xy.x() * 2.0 - 1.0; 78 | double y = xy.y() * 2.0 - 1.0; 79 | double lon = atan2(x, y); 80 | double lat = hypot(x, y) * M_PI - M_PI_2; 81 | return transform::Geo(lon, -lat); 82 | } 83 | static transform::Geo reverse_south_polar(transform::XY xy) { 84 | double x = xy.x() * 2.0 - 1.0; 85 | double y = xy.y() * 2.0 - 1.0; 86 | double lon = atan2(x, -y); 87 | double lat = hypot(x, y) * M_PI - M_PI_2; 88 | return transform::Geo(lon, lat); 89 | } 90 | transform::Geo transform::reverse(transform::XY xy, CRS crs) { 91 | switch (crs) { 92 | case CRS::Equirectangular: 93 | return reverse_equirectangular(xy); 94 | case CRS::Mercator: 95 | return reverse_mercator(xy); 96 | case CRS::North_Polar: 97 | return reverse_north_polar(xy); 98 | case CRS::South_Polar: 99 | return reverse_south_polar(xy); 100 | default: 101 | throw std::out_of_range("Unknown CRS"); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/geo/crs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_GEO_CRS_H_ 20 | #define LEANHRPT_GEO_CRS_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace transform { 27 | /// List of supported projections 28 | enum class CRS { Equirectangular, Mercator, North_Polar, South_Polar }; 29 | /// Human readable names of CRS 30 | const std::vector CRS_NAMES = {"Equirectangular", "Mercator", "North Polar", "South Polar"}; 31 | /// EPSG names of projections 32 | const std::vector CRS_EPSG_NAMES = {"EPSG:4326", "EPSG:3857", "EPSG:3995", "EPSG:3031"}; 33 | 34 | /// Used to enforce stricter typing 35 | using Geo = QPointF; 36 | /// @copydoc Geo 37 | using XY = QPointF; 38 | 39 | /** 40 | * Forward transform, coordinates to pixel 41 | * 42 | * Input in radians, output in XY (0 to 1) 43 | */ 44 | XY forward(Geo geo, CRS crs); 45 | /** 46 | * Reverse transform, pixel coordinates 47 | * 48 | * Input in XY (0 to 1), output in radians 49 | */ 50 | Geo reverse(XY xy, CRS crs); 51 | } // namespace transform 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/geo/geodetic.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_GEO_GEODETIC_H_ 20 | #define LEANHRPT_GEO_GEODETIC_H_ 21 | 22 | #include 23 | 24 | // A data structure containing latitude, longitude and altitude 25 | struct Geodetic { 26 | double latitude; 27 | double longitude; 28 | double altitude; 29 | 30 | public: 31 | Geodetic(double lat, double lon, double alt) : latitude(lat), longitude(lon), altitude(alt) {} 32 | 33 | Geodetic(const predict_position &position) : Geodetic(position.latitude, position.longitude, position.altitude) {} 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/geo/geolocation.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_GEO_GEOLOCATION_H_ 20 | #define LEANHRPT_GEO_GEOLOCATION_H_ 21 | 22 | #include "geodetic.h" 23 | #include "matrix.h" 24 | #include "vector.h" 25 | 26 | struct GeodeticCurve { 27 | public: 28 | GeodeticCurve(double ellipsoidalDistanceMeters, double azimuth, double reverseAzimuth) { 29 | this->EllipsoidalDistanceMeters = ellipsoidalDistanceMeters; 30 | this->Azimuth = azimuth; 31 | this->ReverseAzimuth = reverseAzimuth; 32 | } 33 | double EllipsoidalDistanceMeters; 34 | double Azimuth; 35 | double ReverseAzimuth; 36 | }; 37 | 38 | struct Ellipsoid { 39 | public: 40 | Ellipsoid(double semiMajorAxisMeters, double semiMinorAxisMeters, double flattening, double inverseFlattening) { 41 | this->SemiMajorAxisMeters = semiMajorAxisMeters; 42 | this->SemiMinorAxisMeters = semiMinorAxisMeters; 43 | this->Flattening = flattening; 44 | this->InverseFlattening = inverseFlattening; 45 | } 46 | Ellipsoid(double semiMajorAxisMeters, double inverseFlattening) { 47 | double f = 1.0 / inverseFlattening; 48 | double b = (1.0 - f) * semiMajorAxisMeters; 49 | 50 | Ellipsoid(semiMajorAxisMeters, b, f, inverseFlattening); 51 | } 52 | double SemiMajorAxisMeters; 53 | double SemiMinorAxisMeters; 54 | double Flattening; 55 | double InverseFlattening; 56 | }; 57 | 58 | const Ellipsoid WGS84(6378137.0, 298.257223563); 59 | 60 | GeodeticCurve CalculateGeodeticCurve(Ellipsoid ellipsoid, Geodetic start, Geodetic end, double tolerance = 1e-13); 61 | Vector locationToVector(const Geodetic &location); 62 | Geodetic vectorToLocation(const Vector &vector); 63 | Geodetic los_to_earth(const Geodetic &position, double roll, double pitch, double yaw); 64 | Geodetic los_to_earth(const Vector &position, double roll, double pitch, double yaw); 65 | double calculateBearingAngle(const Geodetic &start, const Geodetic &end); 66 | Matrix4x4 lookAt(const Vector3 &position, const Vector3 &target, const Vector3 &up); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/geo/vector.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Taken from https://github.com/Digitelektro/MeteorDemod 3 | * Its license is as follows: 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2020 Digitelektro 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #ifndef LEANHRPT_GEO_VECTOR_H_ 29 | #define LEANHRPT_GEO_VECTOR_H_ 30 | 31 | struct Vector { 32 | double x; 33 | double y; 34 | double z; 35 | double w; 36 | 37 | public: 38 | Vector() : x(0.0), y(0.0), z(0.0), w(0.0) {} 39 | 40 | Vector(double _x, double _y, double _z) : x(_x), y(_y), z(_z), w(0.0) {} 41 | 42 | double Magnitude() { return sqrt(x * x + y * y + z * z); } 43 | 44 | Vector operator-(const Vector &rhs) { return Vector(x - rhs.x, y - rhs.y, z - rhs.z); } 45 | }; 46 | 47 | struct Vector3 : Vector { 48 | Vector3() : Vector() {} 49 | 50 | Vector3(const Vector v) : Vector(v) {} 51 | 52 | Vector &operator-=(const Vector &v) { 53 | x -= v.x; 54 | y -= v.y; 55 | z -= v.z; 56 | return *this; 57 | } 58 | 59 | Vector3 operator*(double factor) const { 60 | Vector3 r(*this); 61 | r.x *= factor; 62 | r.y *= factor; 63 | r.z *= factor; 64 | return r; 65 | } 66 | 67 | Vector3 &operator*=(double factor) { 68 | x *= factor; 69 | y *= factor; 70 | z *= factor; 71 | return *this; 72 | } 73 | 74 | Vector3 operator/(double factor) const { 75 | Vector3 r(*this); 76 | r.x /= factor; 77 | r.y /= factor; 78 | r.z /= factor; 79 | return r; 80 | } 81 | 82 | Vector3 &operator/=(double factor) { 83 | x /= factor; 84 | y /= factor; 85 | z /= factor; 86 | return *this; 87 | } 88 | 89 | Vector3 Cross(const Vector &v) const { 90 | Vector3 r; 91 | r.x = y * v.z - z * v.y; 92 | r.y = z * v.x - x * v.z; 93 | r.z = x * v.y - y * v.x; 94 | return r; 95 | } 96 | 97 | Vector3 &Normalize() { 98 | double m = Magnitude(); 99 | if (m > 0) { 100 | return (*this) /= m; 101 | } 102 | return *this; 103 | } 104 | 105 | double DistanceSquared() const { return (x * x + y * y + z * z); } 106 | }; 107 | 108 | #endif // VECTOR_H 109 | -------------------------------------------------------------------------------- /src/geometry.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "geometry.h" 20 | 21 | #include 22 | 23 | #include "projection.h" 24 | #include "util.h" 25 | 26 | /// Based off https://github.com/Xerbo/meteor_corrector 27 | QImage correct_geometry(QImage image, SatID satellite, Imager sensor, size_t width) { 28 | const SatelliteInfo satinfo = satellite_info.at(satellite); 29 | const SensorInfo sensorinfo = sensor_info.at(sensor); 30 | 31 | double ratio = (double)width / (double)sensorinfo.width; 32 | const size_t output_width = sensorinfo.swath / sensorinfo.resolution * ratio; 33 | std::vector lut(output_width); 34 | 35 | float view_angle = sensorinfo.swath / EARTH_RADIUS; 36 | float sat_edge = geo::earth2sat_angle(EARTH_RADIUS, satinfo.orbit_height, view_angle / 2); 37 | 38 | // Compute a look up table of pixel positions 39 | for (size_t x = 0; x < output_width; x++) { 40 | float angle = (static_cast(x) / static_cast(output_width) - 0.5f) * view_angle; 41 | angle = geo::earth2sat_angle(EARTH_RADIUS, satinfo.orbit_height, angle); 42 | 43 | lut[x] = (angle / sat_edge + 1.0f) / 2.0f * static_cast(image.width() - 1); 44 | } 45 | 46 | // Copy pixels over from the source to the corrected image 47 | QImage corrected(output_width, image.height(), image.format()); 48 | #pragma omp parallel for 49 | for (size_t y = 0; y < static_cast(image.height()); y++) { 50 | for (size_t x = 0; x < output_width; x++) { 51 | QColor a = image.pixelColor(floor(lut[x]), y); 52 | QColor b = image.pixelColor(ceil(lut[x]), y); 53 | corrected.setPixelColor(x, y, lerp(a, b, fmod(lut[x], 1.0))); 54 | } 55 | } 56 | 57 | return corrected; 58 | } 59 | 60 | void correct_points(std::vector &points, SatID satellite, Imager sensor, size_t width) { 61 | const SatelliteInfo satinfo = satellite_info.at(satellite); 62 | const SensorInfo sensorinfo = sensor_info.at(sensor); 63 | 64 | double ratio = (double)width / (double)sensorinfo.width; 65 | const size_t output_width = sensorinfo.swath / sensorinfo.resolution * ratio; 66 | std::vector lut(output_width); 67 | 68 | float view_angle = sensorinfo.swath / EARTH_RADIUS; 69 | float sat_edge = geo::earth2sat_angle(EARTH_RADIUS, satinfo.orbit_height, view_angle / 2); 70 | 71 | // Copy pixels over from the source to the corrected image 72 | for (QPointF &point : points) { 73 | point.rx() = 74 | geo::sat2earth_angle(EARTH_RADIUS, satinfo.orbit_height, (point.x() / width - 0.5) * 2.0 * sat_edge) / view_angle + 75 | 0.5; 76 | point.rx() *= output_width; 77 | } 78 | } 79 | 80 | void correct_lines(std::vector &lines, SatID satellite, Imager sensor, size_t width) { 81 | correct_points((std::vector &)lines, satellite, sensor, width); 82 | } 83 | -------------------------------------------------------------------------------- /src/geometry.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_GEOMETRY_H_ 20 | #define LEANHRPT_GEOMETRY_H_ 21 | 22 | #include 23 | 24 | #include "satinfo.h" 25 | 26 | QImage correct_geometry(QImage image, SatID satellite, Imager sensor, size_t width); 27 | void correct_points(std::vector &points, SatID satellite, Imager sensor, size_t width); 28 | void correct_lines(std::vector &lines, SatID satellite, Imager sensor, size_t width); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/image/calibration.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "calibration.h" 20 | 21 | #include 22 | 23 | #include "util.h" 24 | 25 | static double str2double(std::string str) { 26 | QLocale l(QLocale::C); 27 | return l.toDouble(QString::fromStdString(str)); 28 | } 29 | 30 | void Calibrator::calibrate(SatID id, Imager imager, std::vector &channels) { 31 | // MSU-MR Calibration 32 | #if 0 33 | if (imager == Imager::MSUMR && d_caldata.count("wl1_sum")) { 34 | for (size_t i = 0; i < 3; i++) { 35 | double wl = d_caldata["wl" + std::to_string(i + 1) + "_sum"] / d_caldata["n"] * 4.0; 36 | double bl = d_caldata["bl" + std::to_string(i + 1) + "_sum"] / d_caldata["n"] * 4.0; 37 | 38 | // Solve for a linear equasion which maps bl to 0 and wl to 100 39 | double a = (100.0 - 0.0) / (wl - bl); 40 | double b = 0.0 - bl * a; 41 | calibrate_linear(i + 1, channels[i], a, b); 42 | } 43 | } 44 | #endif 45 | 46 | for (size_t i = 0; i < channels.size(); i++) { 47 | std::string name = satellite_info.at(id).name + "_" + sensor_info.at(imager).name + "/" + std::to_string(i + 1); 48 | if (!config.sections.count(name)) continue; 49 | 50 | std::map coefficients = config.sections.at(name); 51 | std::string type = "linear"; 52 | if (coefficients.count("type")) { 53 | type = coefficients.at("type"); 54 | } 55 | 56 | if (type == "linear") { 57 | double a = str2double(coefficients.at("a")); 58 | double b = str2double(coefficients.at("b")); 59 | calibrate_linear(i + 1, channels[i], a, b); 60 | } else if (type == "split_linear") { 61 | double a1 = str2double(coefficients.at("a1")); 62 | double b1 = str2double(coefficients.at("b1")); 63 | double a2 = str2double(coefficients.at("a2")); 64 | double b2 = str2double(coefficients.at("b2")); 65 | double c = str2double(coefficients.at("c")); 66 | calibrate_split_linear(i + 1, channels[i], a1, b1, a2, b2, c); 67 | } else if (type == "radiance") { 68 | double ns = str2double(coefficients.at("ns")); 69 | double b0 = str2double(coefficients.at("b0")); 70 | double b1 = str2double(coefficients.at("b1")); 71 | double b2 = str2double(coefficients.at("b2")); 72 | double vc = str2double(coefficients.at("vc")); 73 | double a = str2double(coefficients.at("a")); 74 | double b = str2double(coefficients.at("b")); 75 | calibrate_ir(i + 1, channels[i], ns, b0, b1, b2, vc, a, b); 76 | } 77 | } 78 | } 79 | 80 | void Calibrator::calibrate_linear(size_t ch, QImage &image, double a, double b) { 81 | (void)ch; 82 | 83 | for (int y = 0; y < image.height(); y++) { 84 | quint16 *line = reinterpret_cast(image.scanLine(y)); 85 | 86 | for (int x = 0; x < image.width(); x++) { 87 | double count = line[x] / 64; 88 | count = a * count + b; 89 | line[x] = clamp(count / 100.0, 0.0, 1.0) * UINT16_MAX; 90 | } 91 | } 92 | } 93 | 94 | void Calibrator::calibrate_split_linear(size_t ch, QImage &image, double a1, double b1, double a2, double b2, double c) { 95 | for (int y = 0; y < image.height(); y++) { 96 | quint16 *line = reinterpret_cast(image.scanLine(y)); 97 | if (d_ch3a.size() != 0 && !d_ch3a[y] && ch == 3) continue; 98 | 99 | for (int x = 0; x < image.width(); x++) { 100 | double count = line[x] / 64; 101 | if (count < c) { 102 | count = a1 * count + b1; 103 | } else { 104 | count = a2 * count + b2; 105 | } 106 | line[x] = clamp(count / 100.0, 0.0, 1.0) * UINT16_MAX; 107 | } 108 | } 109 | } 110 | 111 | void Calibrator::calibrate_ir(size_t ch, QImage &image, double Ns, double b0, double b1, double b2, double Vc, double A, 112 | double B) { 113 | double Tbb = d_caldata["blackbody_temperature_sum"] / d_caldata["n"]; // Blackbody temperature 114 | double Tbbstar = A + B * Tbb; // Effective blackbody temperature 115 | 116 | const double c1 = 1.1910427e-5; // mW/(m^2-sr-cm^-4) 117 | const double c2 = 1.4387752; // cm-K 118 | double Nbb = c1 * pow(Vc, 3) / (exp(c2 * Vc / Tbbstar) - 1.0); // Blackbody radiance 119 | 120 | double Cs = d_caldata["ch" + std::to_string(ch) + "_space"] / d_caldata["n"]; // Average space count 121 | double Cbb = d_caldata["ch" + std::to_string(ch) + "_cal"] / d_caldata["n"]; // Average backscan count 122 | 123 | for (int y = 0; y < image.height(); y++) { 124 | quint16 *line = reinterpret_cast(image.scanLine(y)); 125 | 126 | for (int x = 0; x < image.width(); x++) { 127 | double Ce = line[x] / 64; // Earth count 128 | double Nlin = Ns + (Nbb - Ns) * (Cs - Ce) / (Cs - Cbb); // Linear radiance estimate 129 | double Ncor = b0 + b1 * Nlin + b2 * pow(Nlin, 2); // Non-linear correction 130 | double Ne = Nlin + Ncor; // Radiance 131 | 132 | double Testar = c2 * Vc / log(c1 * pow(Vc, 3) / Ne + 1.0); // Equivalent black body temperature 133 | double Te = (Testar - A) / B; // Temperature (kelvin) 134 | 135 | // Convert to celsius 136 | Te -= 273.15; 137 | 138 | Te = (60.0 - Te) / 160.0 * (double)UINT16_MAX; 139 | line[x] = clamp(Te, 0.0, (double)UINT16_MAX); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/image/calibration.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_IMAGE_CALIBRATION_H_ 20 | #define LEANHRPT_IMAGE_CALIBRATION_H_ 21 | 22 | #include 23 | 24 | #include "config/config.h" 25 | #include "satinfo.h" 26 | 27 | class Calibrator { 28 | public: 29 | Calibrator(std::map caldata, std::vector ch3a = {}) 30 | : config("calibration.ini"), d_caldata(caldata), d_ch3a(ch3a){}; 31 | void calibrate(SatID id, Imager imager, std::vector &channel); 32 | 33 | private: 34 | Config config; 35 | std::map d_caldata; 36 | std::vector d_ch3a; 37 | 38 | void calibrate_linear(size_t ch, QImage &image, double a, double b); 39 | void calibrate_split_linear(size_t ch, QImage &image, double a1, double b1, double a2, double b2, double c); 40 | void calibrate_ir(size_t ch, QImage &image, double Ns, double b0, double b1, double b2, double Vc, double A, double B); 41 | }; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/image/compositor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_IMAGE_COMPOSITOR_H_ 20 | #define LEANHRPT_IMAGE_COMPOSITOR_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "image/raw.h" 29 | #include "map.h" 30 | #include "satinfo.h" 31 | #include "util.h" 32 | 33 | enum class Equalization { None, Histogram, Stretch }; 34 | 35 | class ImageCompositor { 36 | public: 37 | /** 38 | * Loads raw data from a Decoder 39 | * 40 | * @param image The actual image data itself 41 | * @param satellite The satellite 42 | * @param sensor The sensor 43 | * @param caldata A map of data containing information used for dynamic calibration 44 | * @param reverse Flips the image vertically, used for reverse transmissions 45 | */ 46 | void import(RawImage *image, SatID satellite, Imager sensor, std::map caldata, double reverse = false); 47 | 48 | /// Get a channel and write the result into `image` 49 | void getChannel(QImage &image, size_t channel); 50 | /// Create a composite and write the result into `image` 51 | void getComposite(QImage &image, std::array chs); 52 | /// @copydoc getComposite 53 | void getComposite(QImage &image, size_t r, size_t g, size_t b) { getComposite(image, {r, g, b}); } 54 | /// Evaluate an expression and write the result into `image` 55 | void getExpression(QImage &image, std::string expression); 56 | 57 | /** 58 | * Adds overlays and final effects to an image 59 | * 60 | * - Geometry correction 61 | * - Map overlays 62 | * - Landmark overlays 63 | * - Flipping 64 | * - IR Blend 65 | */ 66 | void postprocess(QImage &image, bool correct = false); 67 | /** 68 | * Equalises an image 69 | * 70 | * @param image The image to equalise 71 | * @param equalization The type of equalization 72 | * @param clipLimit Clips the histogram, multiplier of the maximum histogram value 73 | * @param brightness_only Only change the brightness of the image 74 | */ 75 | static void equalise(QImage &image, Equalization equalization, float clipLimit, bool brightness_only); 76 | 77 | /// Gets the width of currently loaded image 78 | size_t width() { return m_width; }; 79 | /// Gets the height of currently loaded image 80 | size_t height() { return m_height; }; 81 | /// Gets the number of channels in the currently loaded image 82 | size_t channels() { return m_channels; }; 83 | /// If the image is currently flipped 84 | bool flipped() { return m_isFlipped; } 85 | 86 | /// Set weather the image is flipped or not 87 | void setFlipped(bool state); 88 | /** 89 | * Set weather the image has a thermal overlay 90 | * 91 | * Channel 5 on meteor, 4 on all other satellites 92 | */ 93 | void enableIRBlend(bool enable) { ir_blend = enable; } 94 | 95 | std::vector overlay; 96 | bool enable_map = false; 97 | QColor map_color; 98 | std::vector sunz; 99 | std::vector stops; 100 | std::vector ch3a; 101 | bool has_ch3a = false; 102 | 103 | bool enable_landmarks = false; 104 | QColor landmark_color; 105 | std::vector landmarks; 106 | 107 | private: 108 | size_t m_width; 109 | size_t m_height; 110 | size_t m_channels; 111 | SatID m_satellite; 112 | Imager m_sensor; 113 | bool m_isFlipped; 114 | std::vector rawChannels; 115 | std::map d_caldata; 116 | bool ir_blend = false; 117 | 118 | template 119 | static std::vector create_histogram(QImage &image, float clip_limit = 1.0f); 120 | template 121 | static std::vector create_rgb_histogram(QImage &image, float clip_limit = 1.0f); 122 | 123 | template 124 | static void _equalise(QImage &image, Equalization equalization, std::vector histogram); 125 | static void clip_histogram(std::vector &histogram, float clip_limit); 126 | }; 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /src/image/raw.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "raw.h" 20 | 21 | #include 22 | 23 | #include "protocol/repack.h" 24 | 25 | RawImage::RawImage(size_t width, size_t channels, size_t chunk_size) 26 | : row_buffer(width * channels + 100), m_width(width), m_channels(channels), m_chunk_size(chunk_size), m_rows(0) { 27 | image_buffer.resize(m_channels); 28 | for (size_t i = 0; i < channels; i++) { 29 | image_buffer[i].resize(width * 2000); 30 | } 31 | } 32 | 33 | /// Process interleaved pixels into individual channels 34 | void RawImage::process_line(const uint16_t *data, size_t ch, int offset, int multiplier) { 35 | for (size_t x = 0; x < m_width; x++) { 36 | image_buffer[ch][m_rows * m_width + x] = data[x * m_channels + ch + offset] * multiplier; 37 | } 38 | } 39 | 40 | /// Process interleaved chunks of pixels into individual channels 41 | void RawImage::process_line_chunked(const uint16_t *data, size_t ch, int offset, int multiplier) { 42 | for (size_t x = 0; x < m_width; x += m_chunk_size) { 43 | for (size_t i = 0; i < m_chunk_size; i++) { 44 | image_buffer[ch][m_rows * m_width + x + i] = data[x * m_channels + ch * m_chunk_size + i + offset] * multiplier; 45 | } 46 | } 47 | } 48 | 49 | void RawImage::push10Bit(const uint8_t *data, int offset) { 50 | int byte_offset = offset / 4 * 5; // Offset as close as possible by byte shifting 51 | int pixel_offset = offset % 4; // Numbers of pixels to offset after byte shifting 52 | 53 | repack10(&data[byte_offset], row_buffer.data(), m_width * m_channels + pixel_offset); 54 | push16Bit(row_buffer.data(), pixel_offset, 64); 55 | } 56 | 57 | void RawImage::push16Bit(const uint16_t *data, int offset, int multiplier) { 58 | for (size_t ch = 0; ch < m_channels; ch++) { 59 | if (m_chunk_size == 1) { 60 | process_line(data, ch, offset, multiplier); 61 | } else { 62 | process_line_chunked(data, ch, offset, multiplier); 63 | } 64 | } 65 | 66 | set_height(m_rows + 1); 67 | } 68 | -------------------------------------------------------------------------------- /src/image/raw.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_IMAGE_RAW_H_ 20 | #define LEANHRPT_IMAGE_RAW_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | class RawImage { 27 | public: 28 | RawImage(size_t width, size_t channels, size_t chunk_size = 1); 29 | 30 | void push10Bit(const uint8_t *data, int offset = 0); 31 | void push16Bit(const uint16_t *data, int offset = 0, int multiplier = 1); 32 | 33 | uint16_t *getChannel(size_t channel) { 34 | if (channel < m_channels) { 35 | return image_buffer[channel].data(); 36 | } 37 | throw std::runtime_error("Channel index out of range"); 38 | } 39 | 40 | size_t width() { return m_width; } 41 | size_t channels() { return m_channels; } 42 | size_t rows() { return m_rows; } 43 | void set_height(size_t new_height) { 44 | m_rows = new_height; 45 | 46 | for (auto &channel : image_buffer) { 47 | if (channel.size() > m_rows * m_width) continue; 48 | channel.resize((m_rows + 2000) * m_width); 49 | } 50 | } 51 | 52 | private: 53 | std::vector row_buffer; 54 | std::vector> image_buffer; 55 | 56 | void process_line(const uint16_t *data, size_t ch, int offset, int multiplier); 57 | void process_line_chunked(const uint16_t *data, size_t ch, int offset, int multiplier); 58 | 59 | size_t m_width; 60 | size_t m_channels; 61 | size_t m_chunk_size; 62 | size_t m_rows; 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | #include "commandline.h" 22 | #include "mainwindow.h" 23 | 24 | int main(int argc, char *argv[]) { 25 | QApplication app(argc, argv); 26 | QApplication::setApplicationName("LeanHRPT Decode"); 27 | QApplication::setApplicationVersion(VERSION); 28 | 29 | QCommandLineParser parser; 30 | parser.setApplicationDescription("LeanHRPT Decode - A high quality, easy to use HRPT decoder."); 31 | parser.addHelpOption(); 32 | parser.addVersionOption(); 33 | 34 | parser.addOption( 35 | {{"i", "ini"}, 36 | "Path to an ini containing composite definitions (https://github.com/Xerbo/LeanHRPT-Decode/wiki/Command-Line)", 37 | "ini"}); 38 | parser.addOption({{"o", "out"}, "Write images into ", "out"}); 39 | parser.addOption({{"f", "flip"}, "Flip the image"}); 40 | parser.addOption({{"m", "map"}, "Path to a shapefile", "path"}); 41 | parser.addOption({{"l", "landmark"}, "Path to a landmarks file", "path"}); 42 | parser.addPositionalArgument("file", "filename"); 43 | parser.process(app); 44 | 45 | if (parser.positionalArguments().isEmpty()) { 46 | MainWindow window; 47 | window.show(); 48 | return app.exec(); 49 | } else { 50 | return parseCommandLine(parser); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/map.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_MAP_H_ 20 | #define LEANHRPT_MAP_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "geo/crs.h" 29 | #include "projection.h" 30 | 31 | struct Landmark { 32 | QPointF geo; 33 | QString text; 34 | }; 35 | 36 | namespace map { 37 | // Checks that a Shapefile is readable and supported (Polyline/Polygon) 38 | bool verify_shapefile(std::string filename); 39 | 40 | // Decompose a Polyline/Polygon Shapefile into a list of line segments 41 | std::vector read_shapefile(std::string filename); 42 | // Read a landmark CSV file 43 | std::vector read_landmarks(std::string filename); 44 | 45 | // Sort line segments into 10x10 "buckets" 46 | std::array, 36 * 18> index_line_segments(const std::vector &line_segments); 47 | 48 | // Warp an (indexed) map to fit a pass based off a point grid 49 | std::vector warp_to_pass(const std::array, 36 * 18> &buckets, 50 | const std::vector> &points, size_t xn); 51 | std::vector warp_to_pass(const std::vector &landmarks, const std::vector> &points, 52 | size_t xn); 53 | 54 | // Project a pass into Rectangular projection 55 | QImage project(const QImage &image, const std::vector> &points, size_t xn, QSize resolution, 56 | QRectF bounds); 57 | 58 | QImage reproject(const QImage &image, transform::CRS crs, QRectF source_bounds, QRectF target_bounds); 59 | 60 | // Render a map overlay on an image with Rectangular projection 61 | void add_overlay(QImage &image, std::vector &line_segments, QColor color, transform::CRS crs, QRectF bounds); 62 | void add_landmarks(QImage &image, const std::vector &landmarks, QColor color, transform::CRS crs, QRectF bounds); 63 | 64 | // Calculate bounds of a pass, height is inverted 65 | QRectF bounds(const std::vector> &points); 66 | QRectF bounds_crs(const std::vector> &points, transform::CRS crs); 67 | } // namespace map 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/network.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "network.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "config/config.h" 29 | 30 | TLEManager::TLEManager() { 31 | QString path = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/weather.txt"; 32 | quint64 time = QDateTime::currentSecsSinceEpoch(); 33 | quint64 modified = QFileInfo(path).lastModified().toSecsSinceEpoch(); 34 | 35 | if (time - modified > 24 * 60 * 60) { 36 | qInfo() << "TLEManager: Downloading fresh TLEs"; 37 | QNetworkAccessManager *manager = new QNetworkAccessManager(); 38 | QNetworkAccessManager::connect(manager, &QNetworkAccessManager::finished, [this, path](QNetworkReply *reply) { 39 | if (reply->error() != QNetworkReply::NoError) { 40 | return; 41 | } 42 | 43 | QFile tle(path); 44 | tle.open(QIODevice::WriteOnly | QIODevice::Text); 45 | while (!reply->atEnd()) { 46 | tle.write(reply->read(1024)); 47 | } 48 | tle.close(); 49 | qInfo() << "TLEManager: Downloaded finished"; 50 | 51 | parse(path.toStdString()); 52 | }); 53 | 54 | QNetworkRequest request; 55 | request.setUrl(QUrl(TLE_URL)); 56 | request.setRawHeader("User-Agent", USER_AGENT); 57 | manager->get(request); 58 | } else { 59 | qInfo() << "TLEManager: Reusing existing TLEs"; 60 | parse(path.toStdString()); 61 | } 62 | } 63 | 64 | void TLEManager::parse(std::string filename) { 65 | QFile tle(QString::fromStdString(filename)); 66 | tle.open(QIODevice::ReadOnly | QIODevice::Text); 67 | 68 | while (!tle.atEnd()) { 69 | std::string name = tle.readLine().simplified().toStdString(); 70 | QString line1 = tle.readLine(); 71 | QString line2 = tle.readLine(); 72 | int norad = line2.split(" ")[1].toInt(); 73 | 74 | catalog.insert({name, {line1.toStdString(), line2.toStdString()}}); 75 | catalog_by_norad.insert({norad, {line1.toStdString(), line2.toStdString()}}); 76 | } 77 | } 78 | 79 | UpdateChecker::UpdateChecker() { 80 | if (std::string(VERSION).find("-") != std::string::npos || std::string(VERSION) == "Unknown") return; 81 | 82 | qInfo() << "UpdateChecker: Checking for updates"; 83 | QNetworkAccessManager *manager = new QNetworkAccessManager(); 84 | QNetworkAccessManager::connect(manager, &QNetworkAccessManager::finished, [this](QNetworkReply *reply) { 85 | if (reply->error() != QNetworkReply::NoError) { 86 | return; 87 | } 88 | 89 | QJsonArray tags = QJsonDocument::fromJson(reply->readAll()).array(); 90 | QString latest_version = tags.at(0)["name"].toString(); 91 | if (latest_version != VERSION) { 92 | QString url = QString("https://github.com/Xerbo/LeanHRPT-Decode/releases/tag/%1").arg(latest_version); 93 | qInfo() << "UpdateChecker: An update is available" << url; 94 | updateAvailable(url); 95 | } 96 | }); 97 | 98 | QNetworkRequest request; 99 | request.setUrl(QUrl(TAG_URL)); 100 | request.setRawHeader("User-Agent", USER_AGENT); 101 | manager->get(request); 102 | } 103 | -------------------------------------------------------------------------------- /src/network.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_TLE_H_ 20 | #define LEANHRPT_TLE_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #define TLE_URL "https://celestrak.org/NORAD/elements/weather.txt" 28 | #define TAG_URL "https://api.github.com/repos/xerbo/LeanHRPT-Decode/tags" 29 | #define USER_AGENT ("LeanHRPT-Decode/" VERSION " (https://github.com/Xerbo/LeanHRPT-Decode)") 30 | 31 | // Super simple class that downloads and parses TLEs in the background 32 | class TLEManager { 33 | public: 34 | TLEManager(); 35 | 36 | /** 37 | * Get the currently loaded catalog of TLEs 38 | * 39 | * @returns A map of the object name to the actual elements 40 | */ 41 | std::map> catalog; 42 | /** 43 | * Get the currently loaded catalog of TLEs 44 | * 45 | * @returns A map of the NORAD ID to the actual elements 46 | */ 47 | std::map> catalog_by_norad; 48 | 49 | private: 50 | void parse(std::string filename); 51 | }; 52 | 53 | class UpdateChecker : public QObject { 54 | Q_OBJECT 55 | public: 56 | UpdateChecker(); 57 | signals: 58 | void updateAvailable(QString url); 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/orbit.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_ORBIT_H_ 20 | #define LEANHRPT_ORBIT_H_ 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | // A simple C++ wrapper around libpredict 29 | class OrbitPredictor { 30 | public: 31 | OrbitPredictor(std::pair tle) { 32 | std::setlocale(LC_NUMERIC, "C"); 33 | orbital_elements = predict_parse_tle(tle.first.c_str(), tle.second.c_str()); 34 | std::setlocale(LC_NUMERIC, ""); 35 | } 36 | ~OrbitPredictor() { predict_destroy_orbital_elements(orbital_elements); } 37 | 38 | /** 39 | * Gets orbital information at the specified timestamp 40 | * 41 | * @param timestamp UNIX timestamp 42 | * @return A predict_position struct 43 | */ 44 | struct predict_position predict(double timestamp) { 45 | // UNIX to Julian date 46 | predict_julian_date_t prediction_time = (timestamp / 86400.0) - 3651.0; 47 | 48 | struct predict_position orbit; 49 | if (predict_orbit(orbital_elements, &orbit, prediction_time)) { 50 | throw std::runtime_error("Error predicting orbit"); 51 | } 52 | 53 | return orbit; 54 | } 55 | 56 | private: 57 | predict_orbital_elements_t *orbital_elements; 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/projectdialog.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "projectdialog.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "map.h" 26 | #include "qt/ui_projectdialog.h" 27 | 28 | ProjectDialog::ProjectDialog(QWidget *parent) : QDialog(parent) { 29 | ui = new Ui::ProjectDialog; 30 | ui->setupUi(this); 31 | 32 | scene = new QGraphicsScene(this); 33 | ui->projectionPreview->setScene(scene); 34 | 35 | render_finished = new QFutureWatcher(this); 36 | QFutureWatcher::connect(render_finished, &QFutureWatcher::finished, [=]() { ui->render->setEnabled(true); }); 37 | 38 | for (const std::string &crs : transform::CRS_NAMES) { 39 | ui->projection->addItem(QString::fromStdString(crs)); 40 | } 41 | } 42 | 43 | ProjectDialog::~ProjectDialog() { delete ui; } 44 | 45 | void ProjectDialog::write_wld_file(QString filename) { 46 | QFile file(filename); 47 | if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return; 48 | 49 | QTextStream out(&file); 50 | 51 | out << pixelsize << "\n"; 52 | out << 0.0 << "\n"; 53 | out << 0.0 << "\n"; 54 | out << -pixelsize << "\n"; 55 | out << bounds.left() << "\n"; 56 | out << bounds.bottom() << "\n"; 57 | } 58 | 59 | void ProjectDialog::write_pam_file(QString filename, std::string srs) { 60 | QFile file(filename); 61 | if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return; 62 | 63 | QTextStream out(&file); 64 | out << ""; 65 | out << QString::fromStdString(srs); 66 | out << ""; 67 | } 68 | 69 | QSize ProjectDialog::calculate_dimensions(size_t resolution) { 70 | bounds = QRectF(-180, -90, 360, 180); 71 | target_bounds = QRectF(0, 0, 1, 1); 72 | if (ui->bounds->currentText() == "Auto") { 73 | bounds = map::bounds(get_points(31)); 74 | target_bounds = map::bounds_crs(get_points(31), crs); 75 | } 76 | 77 | // So big it might as well be the entire earth 78 | if (bounds.width() * bounds.height() > (360 * 180) * 0.95) { 79 | bounds = QRectF(-180, -90, 360, 180); 80 | target_bounds = QRectF(0, 0, 1, 1); 81 | } 82 | 83 | double scale = EARTH_CIRCUMFERENCE / ui->resolution->value(); 84 | pixelsize = 360.0 / scale; 85 | QSize dimensions(bounds.width() / 360.0 * scale, bounds.height() / 360.0 * scale); 86 | if (crs != transform::CRS::Equirectangular) { 87 | dimensions.rheight() = dimensions.width() * (double)target_bounds.height() / (double)target_bounds.width(); 88 | } 89 | ui->details->setText(QString("Final size: %1x%2").arg(dimensions.width()).arg(dimensions.height())); 90 | 91 | if (resolution != 0) { 92 | dimensions = QSize(resolution * 2, resolution); 93 | } 94 | 95 | return dimensions; 96 | } 97 | 98 | QImage ProjectDialog::render(QSize dimensions) { 99 | QImage image = map::project(get_viewport(), get_points(31), 31, dimensions, bounds); 100 | image = map::reproject(image, crs, bounds, target_bounds); 101 | 102 | if (map_enable()) { 103 | std::vector map = map::read_shapefile(map_shapefile().toStdString()); 104 | map::add_overlay(image, map, map_color(), crs, target_bounds); 105 | } 106 | if (landmark_enable()) { 107 | std::vector landmarks = map::read_landmarks(landmark_file().toStdString()); 108 | map::add_landmarks(image, landmarks, landmark_color(), crs, target_bounds); 109 | } 110 | 111 | return image; 112 | } 113 | 114 | void ProjectDialog::on_preview_clicked() { 115 | QImage image = render(calculate_dimensions(1000)); 116 | scene->clear(); 117 | scene->setSceneRect(0, 0, image.width(), image.height()); 118 | scene->addPixmap(QPixmap::fromImage(image, Qt::NoFormatConversion)); 119 | ui->projectionPreview->fitInView(scene->sceneRect(), Qt::KeepAspectRatio); 120 | } 121 | 122 | void ProjectDialog::on_render_clicked() { 123 | QSize dimensions = calculate_dimensions(0); 124 | if (dimensions.width() * dimensions.height() > 64000000) { 125 | QMessageBox confirm; 126 | confirm.setText(QString("Generating a large (%1x%2) image, this may cause slowdowns/crashes!") 127 | .arg(dimensions.width()) 128 | .arg(dimensions.height())); 129 | confirm.setStandardButtons(QMessageBox::Cancel | QMessageBox::Ok); 130 | confirm.setDefaultButton(QMessageBox::Cancel); 131 | confirm.setIcon(QMessageBox::Warning); 132 | if (confirm.exec() == QMessageBox::Cancel) { 133 | return; 134 | } 135 | } 136 | 137 | QString filename = QFileDialog::getSaveFileName( 138 | this, "Save Projected Image", 139 | QString("%1_%2.png").arg(default_filename()).arg(QString::fromStdString(transform::CRS_NAMES[(size_t)crs])), 140 | "PNG (*.png);;JPEG (*.jpg *.jpeg);;WEBP (*.webp);;BMP (*.bmp)"); 141 | if (filename.isEmpty()) return; 142 | 143 | QFuture future = QtConcurrent::run([=]() { 144 | QImage image = render(dimensions); 145 | if (crs == transform::CRS::Equirectangular) { 146 | QFileInfo fi(filename); 147 | write_wld_file(fi.absolutePath() + "/" + fi.completeBaseName() + ".wld"); 148 | write_pam_file(filename + ".aux.xml", transform::CRS_EPSG_NAMES[(size_t)crs]); 149 | } 150 | image.save(filename); 151 | }); 152 | 153 | render_finished->setFuture(future); 154 | ui->render->setEnabled(false); 155 | } 156 | 157 | void ProjectDialog::resizeEvent(QResizeEvent *event) { 158 | Q_UNUSED(event); 159 | ui->projectionPreview->fitInView(scene->sceneRect(), Qt::KeepAspectRatio); 160 | } 161 | 162 | void ProjectDialog::on_projection_textActivated(QString text) { 163 | for (size_t i = 0; i < transform::CRS_NAMES.size(); i++) { 164 | if (text.toStdString() == transform::CRS_NAMES[i]) { 165 | crs = (transform::CRS)i; 166 | return; 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/projectdialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROJECTDIALOG_H_ 20 | #define LEANHRPT_PROJECTDIALOG_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "geo/crs.h" 30 | #include "projection.h" 31 | #include "satinfo.h" 32 | 33 | QT_BEGIN_NAMESPACE 34 | namespace Ui { 35 | class ProjectDialog; 36 | } 37 | QT_END_NAMESPACE 38 | 39 | class ProjectDialog : public QDialog { 40 | Q_OBJECT 41 | public: 42 | ProjectDialog(QWidget *parent = nullptr); 43 | ~ProjectDialog(); 44 | 45 | private: 46 | Ui::ProjectDialog *ui; 47 | QGraphicsScene *scene; 48 | QFutureWatcher *render_finished; 49 | QImage render(QSize dimensions); 50 | QSize calculate_dimensions(size_t resolution); 51 | void write_wld_file(QString filename); 52 | void write_pam_file(QString filename, std::string srs); 53 | 54 | transform::CRS crs = transform::CRS::Equirectangular; 55 | QRectF bounds; 56 | QRectF target_bounds; 57 | double pixelsize; 58 | 59 | virtual void resizeEvent(QResizeEvent *event) override; 60 | private slots: 61 | void on_preview_clicked(); 62 | void on_render_clicked(); 63 | void on_projection_textActivated(QString text); 64 | signals: 65 | /// @cond 66 | QImage get_viewport(); 67 | std::vector> get_points(size_t n); 68 | 69 | QString map_shapefile(); 70 | QColor map_color(); 71 | bool map_enable(); 72 | 73 | QString landmark_file(); 74 | QColor landmark_color(); 75 | bool landmark_enable(); 76 | 77 | QString default_filename(); 78 | /// @endcond 79 | }; 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /src/projection.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROJECTION_H_ 20 | #define LEANHRPT_PROJECTION_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "geo/geodetic.h" 27 | #include "orbit.h" 28 | #include "satinfo.h" 29 | #include "util.h" 30 | 31 | const double EARTH_RADIUS = 6371.0; 32 | const double EARTH_CIRCUMFERENCE = EARTH_RADIUS * 2.0 * M_PI; 33 | using xy = std::pair; 34 | 35 | namespace geo { 36 | // Convert from a internal angle of a circle to the viewing angle of a point above the circle. 37 | inline double earth2sat_angle(double radius, double height, double angle) { 38 | return -atan(sin(angle) * radius / (cos(angle) * radius - (radius + height))); 39 | } 40 | inline double sat2earth_angle(double radius, double height, double angle) { 41 | return asin((radius + height) / radius * sin(angle)) - angle; 42 | } 43 | } // namespace geo 44 | 45 | class Projector { 46 | public: 47 | Projector(std::pair tle) : predictor(tle) {} 48 | 49 | /** 50 | * Create a GCP grid 51 | * 52 | * @returns A vector containing geographical tags for an image 53 | */ 54 | std::vector> calculate_gcps(const std::vector ×tamps, size_t pointsy, size_t pointsx, 55 | Imager sensor, SatID sat, size_t width); 56 | 57 | /** 58 | * Saves GCPs into a file, format is `x,y,lat,lon` 59 | */ 60 | void save_gcp_file(const std::vector ×tamps, size_t pointsy, size_t pointsx, Imager sensor, SatID sat, 61 | std::string filename, size_t width); 62 | std::vector calculate_sunz(const std::vector ×tamps, Imager sensor, SatID sat, size_t width); 63 | 64 | /** 65 | * If the provided timestamps represent a northbound pass 66 | * 67 | * Internally this takes the lower and upper quartile positions, and calculates the azimuth from them 68 | */ 69 | bool is_northbound(const std::vector ×tamps); 70 | 71 | private: 72 | std::vector> calculate_scan(const Geodetic &position, double azimuth, double fov, double roll, 73 | double pitch, double pitchscale, bool curved, size_t n); 74 | 75 | OrbitPredictor predictor; 76 | }; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/protocol/ccsds/deframer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROTOCOL_CCSDS_DEFRAMER_H_ 20 | #define LEANHRPT_PROTOCOL_CCSDS_DEFRAMER_H_ 21 | 22 | #include 23 | #include 24 | 25 | namespace ccsds { 26 | enum class SyncMachineState { State0, State1, State2, State3 }; 27 | 28 | using asm_t = uint32_t; 29 | 30 | /// A deframer based on http://www.sat.cc.ua/data/CADU%20Frame%20Synchro.pdf 31 | class Deframer { 32 | public: 33 | Deframer(); 34 | ~Deframer(); 35 | bool work(const uint8_t *in, uint8_t *out, size_t len); 36 | 37 | private: 38 | asm_t shifter; 39 | bool asmCompare(asm_t a, asm_t b); 40 | 41 | uint8_t *frameBuffer; 42 | uint8_t byteBuffer; 43 | unsigned int bufferPosition = 0, bufferBitPosition = 0; 44 | void pushByte(uint8_t byte); 45 | void pushBit(bool bit); 46 | 47 | SyncMachineState state; 48 | 49 | unsigned int bitsWritten; 50 | bool writingData; 51 | bool invert; 52 | unsigned int badFrames; 53 | unsigned int goodFrames; 54 | 55 | void startWriting(); 56 | void enterState(SyncMachineState newState); 57 | int skip; 58 | }; 59 | } // namespace ccsds 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/protocol/ccsds/demuxer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "demuxer.h" 20 | 21 | #include 22 | #include 23 | 24 | #define HEADER_LEN 6 25 | 26 | namespace ccsds { 27 | std::vector SimpleDemuxer::work(const uint8_t *in) { 28 | std::vector packet; 29 | uint16_t fhp = (in[fhp_offset] << 8 | in[fhp_offset + 1]) & 0b11111111111; 30 | const uint8_t *data = &in[fhp_offset + 2]; 31 | 32 | // Exit immediately if the FHP is invalid 33 | if (fhp > mpdu_size && fhp != 2047) return packet; 34 | 35 | // MPDU with just data, no CPPDU header 36 | if (writingData && fhp == 2047) { 37 | packetBuffer.insert(packetBuffer.end(), data, &data[mpdu_size]); 38 | } 39 | // End of a CPPDU frame 40 | if (writingData && fhp != 2047) { 41 | packetBuffer.insert(packetBuffer.end(), data, &data[fhp]); 42 | writingData = false; 43 | packet = packetBuffer; 44 | packetBuffer.clear(); 45 | } 46 | // A new CPPDU frame 47 | if (!writingData && fhp != 2047) { 48 | packetBuffer.insert(packetBuffer.end(), &data[fhp], &data[mpdu_size]); 49 | writingData = true; 50 | } 51 | 52 | return packet; 53 | } 54 | 55 | std::vector> Demuxer::work(const uint8_t *in) { 56 | std::vector> packets; 57 | 58 | while (true) { 59 | DemuxerStatus state = internal_work(in); 60 | 61 | if (state == DemuxerStatus::PARSED) { 62 | packets.push_back(packet); 63 | } else if (state == DemuxerStatus::PROCEED) { 64 | break; 65 | } 66 | } 67 | 68 | return packets; 69 | } 70 | DemuxerStatus Demuxer::internal_work(const uint8_t *in) { 71 | uint16_t fhp = (in[fhp_offset] << 8 | in[fhp_offset + 1]) & 0b11111111111; 72 | const uint8_t *data = &in[fhp_offset + 2]; 73 | 74 | // Exit immediately if the FHP is invalid 75 | if (fhp > mpdu_size && fhp != 2047) return DemuxerStatus::PROCEED; 76 | 77 | size_t bytes_left = 0; 78 | bool jump_idle = (fhp != 2047 && offset == 0); 79 | 80 | switch (state) { 81 | case DemuxerState::IDLE: 82 | // Get the pointer to the next header, set that as the new offset 83 | if (fhp != 2047) { 84 | offset = fhp; 85 | frag_offset = 0; 86 | state = DemuxerState::HEADER; 87 | return DemuxerStatus::FRAGMENT; 88 | } 89 | break; 90 | case DemuxerState::HEADER: 91 | bytes_left = HEADER_LEN - frag_offset; 92 | 93 | if (offset + bytes_left < mpdu_size) { 94 | // The header's end byte is contained in this VCDU: copy bytes 95 | std::copy(data + offset, data + offset + bytes_left, packet.begin() + frag_offset); 96 | frag_offset = 0; 97 | offset += bytes_left; 98 | state = DemuxerState::DATA; 99 | return DemuxerStatus::FRAGMENT; 100 | } 101 | 102 | // The header's end byte is in the next VCDU: copy some bytes and update the fragment offset 103 | std::copy(data + offset, data + mpdu_size, packet.begin() + frag_offset); 104 | frag_offset += mpdu_size - offset; 105 | offset = 0; 106 | return DemuxerStatus::PROCEED; 107 | case DemuxerState::DATA: 108 | bytes_left = CPPDUHeader(packet).length - frag_offset; 109 | 110 | if (offset + bytes_left < mpdu_size) { 111 | // The end of this data segment is within the VCDU: copy bytes 112 | std::copy(data + offset, data + offset + bytes_left, packet.begin() + HEADER_LEN + frag_offset); 113 | frag_offset = 0; 114 | offset += bytes_left; 115 | state = jump_idle ? DemuxerState::IDLE : DemuxerState::HEADER; 116 | return DemuxerStatus::PARSED; 117 | } 118 | 119 | // The data continues in the next VCDU: copy some bytes and update the fragment offset 120 | std::copy(data + offset, data + mpdu_size, packet.begin() + HEADER_LEN + frag_offset); 121 | frag_offset += mpdu_size - offset; 122 | offset = 0; 123 | state = jump_idle ? DemuxerState::IDLE : DemuxerState::DATA; 124 | return jump_idle ? DemuxerStatus::FRAGMENT : DemuxerStatus::PROCEED; 125 | } 126 | 127 | return DemuxerStatus::PROCEED; 128 | } 129 | } // namespace ccsds 130 | -------------------------------------------------------------------------------- /src/protocol/ccsds/demuxer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROTOCOL_CCSDS_DEMUXER_H_ 20 | #define LEANHRPT_PROTOCOL_CCSDS_DEMUXER_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace ccsds { 27 | struct CPPDUHeader { 28 | uint16_t apid; 29 | uint8_t sequence_flag; 30 | uint16_t counter; 31 | uint16_t length; 32 | 33 | CPPDUHeader(const uint8_t *header) { 34 | apid = (header[0] << 8 | header[1]) & 0b11111111111; 35 | sequence_flag = (header[2] << 8 | header[3]) >> 14; 36 | counter = (header[2] << 8 | header[3]) & 0b11111111111111; 37 | length = (header[4] << 8 | header[5]) + 1; 38 | } 39 | CPPDUHeader(const std::vector &header) : CPPDUHeader(header.data()) {} 40 | }; 41 | 42 | /// A (fast) demuxer that can only handle one packet per frame 43 | class SimpleDemuxer { 44 | public: 45 | SimpleDemuxer(bool insert_zone = true) : fhp_offset(insert_zone ? 12 : 10), mpdu_size(insert_zone ? 882 : 884) {} 46 | std::vector work(const uint8_t *in); 47 | 48 | private: 49 | const size_t fhp_offset; 50 | const size_t mpdu_size; 51 | 52 | bool writingData = false; 53 | std::vector packetBuffer; 54 | }; 55 | 56 | enum class DemuxerState { IDLE, HEADER, DATA }; 57 | enum class DemuxerStatus { PROCEED, FRAGMENT, PARSED }; 58 | 59 | /// Demuxer that can handle an arbitrary amount of packets per frame 60 | class Demuxer { 61 | public: 62 | Demuxer(bool insert_zone = true) : fhp_offset(insert_zone ? 12 : 10), mpdu_size(insert_zone ? 882 : 884), packet(65536) {} 63 | std::vector> work(const uint8_t *in); 64 | 65 | private: 66 | const size_t fhp_offset; 67 | const size_t mpdu_size; 68 | 69 | DemuxerState state = DemuxerState::IDLE; 70 | uint16_t offset = 0; 71 | uint16_t frag_offset = 0; 72 | std::vector packet; 73 | 74 | DemuxerStatus internal_work(const uint8_t *in); 75 | }; 76 | } // namespace ccsds 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/protocol/deframer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "deframer.h" 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | // Constructor 26 | template 27 | ArbitraryDeframer::ArbitraryDeframer(unsigned int incorrectBitThreshold, bool checkInverted) 28 | : frameBuffer(new uint8_t[FRAME_SIZE / 8]), checkInverted(checkInverted), incorrectBitThreshold(incorrectBitThreshold) { 29 | if (ASM_SIZE > sizeof(ASM_T) * 8) { 30 | throw std::runtime_error("ArbitraryDeframer: ASM size larger than what ASM_T allows"); 31 | } 32 | } 33 | 34 | template 35 | ArbitraryDeframer::~ArbitraryDeframer() { 36 | delete[] frameBuffer; 37 | } 38 | 39 | // Push a byte into the buffer 40 | template 41 | void ArbitraryDeframer::pushByte(uint8_t byte) { 42 | frameBuffer[bufferPosition++] = byte; 43 | } 44 | 45 | // Push a bit into the buffer 46 | template 47 | void ArbitraryDeframer::pushBit(bool bit) { 48 | byteBuffer = byteBuffer << 1 | bit; 49 | bufferBitPosition++; 50 | 51 | if (bufferBitPosition == 8) { 52 | pushByte(byteBuffer); 53 | bufferBitPosition = 0; 54 | } 55 | } 56 | 57 | template 58 | bool ArbitraryDeframer::fuzzyBitCompare(ASM_T a, ASM_T b, size_t threshold) { 59 | return std::bitset(a ^ b).count() <= threshold; 60 | } 61 | 62 | // Push a bit into the buffer 63 | template 64 | void ArbitraryDeframer::startWriting() { 65 | bufferPosition = 0; 66 | bufferBitPosition = 0; 67 | bitsWritten = 0; 68 | writingData = true; 69 | } 70 | 71 | // Work function 72 | template 73 | bool ArbitraryDeframer::work(const uint8_t *data, uint8_t *out, unsigned int len) { 74 | bool complete_frame = false; 75 | 76 | for (size_t i = 0; i < len; i++) { 77 | for (int j = 7; j >= 0; j--) { 78 | bool bit = std::bitset(data[i]).test(j); 79 | if (invert) bit = !bit; 80 | 81 | shifter = shifter << 1 | bit; 82 | 83 | if (writingData) { 84 | // Append syncword, backwards 85 | if (bitsWritten == 0) { 86 | for (int ASMBit = ASM_SIZE - 1; ASMBit >= 0; ASMBit--) { 87 | pushBit(std::bitset(ASM).test(ASMBit)); 88 | } 89 | bitsWritten += ASM_SIZE; 90 | } 91 | 92 | pushBit(bit); 93 | bitsWritten++; 94 | 95 | // At the end of a frame, copy the data into the output pointer and reset 96 | if (bitsWritten == FRAME_SIZE) { 97 | writingData = false; 98 | bitsWritten = 0; 99 | 100 | complete_frame = 1; 101 | std::memcpy(out, frameBuffer, FRAME_SIZE / 8); 102 | 103 | continue; 104 | } 105 | } 106 | 107 | if (!writingData) { 108 | if (fuzzyBitCompare(shifter, ASM, incorrectBitThreshold)) { 109 | startWriting(); 110 | } else if (checkInverted && fuzzyBitCompare(shifter, ~ASM, incorrectBitThreshold)) { 111 | startWriting(); 112 | invert = !invert; 113 | } 114 | } 115 | } 116 | } 117 | 118 | return complete_frame; 119 | } 120 | 121 | // Standard 8192 bit CCSDS frames 122 | // DEPRECATED: Use ccsds::Deframer instead 123 | // template class ArbitraryDeframer; 124 | 125 | /// Meteor MSU-MR 126 | template class ArbitraryDeframer; 127 | /// Meteor Telemetry 128 | template class ArbitraryDeframer; 129 | /// Meteor MTVZA 130 | template class ArbitraryDeframer; 131 | 132 | /// NOAA HRPT 133 | template class ArbitraryDeframer; 134 | /// NOAA GAC 135 | template class ArbitraryDeframer; 136 | /// NOAA GAC, reverse, two bits appended in front to achieve byte alignment 137 | template class ArbitraryDeframer; 138 | /// NOAA DSB 139 | /// template class ArbitraryDeframer; 140 | 141 | /// Fengyun VIRR 142 | template class ArbitraryDeframer; 143 | -------------------------------------------------------------------------------- /src/protocol/deframer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROTOCOL_DEFRAMER_H_ 20 | #define LEANHRPT_PROTOCOL_DEFRAMER_H_ 21 | 22 | #include 23 | #include 24 | 25 | template 26 | class ArbitraryDeframer { 27 | public: 28 | ArbitraryDeframer(unsigned int incorrectBitThreshold = 10, bool checkInverted = false); 29 | ~ArbitraryDeframer(); 30 | bool work(const uint8_t *data, uint8_t *out, unsigned int len); 31 | 32 | private: 33 | uint8_t *frameBuffer; 34 | 35 | // Options 36 | bool checkInverted; 37 | unsigned int incorrectBitThreshold; 38 | 39 | // Used for loading data into `frameBuffer` 40 | uint8_t byteBuffer; 41 | int bufferPosition = 0, bufferBitPosition = 0; 42 | void pushByte(uint8_t byte); 43 | void pushBit(bool bit); 44 | 45 | // Actually used for deframing 46 | ASM_T shifter; 47 | unsigned int bitsWritten = 0; 48 | bool writingData = false; 49 | bool invert = false; 50 | void startWriting(); 51 | bool fuzzyBitCompare(ASM_T a, ASM_T b, size_t threshold); 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /src/protocol/lrpt/huffman.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "huffman.h" 20 | 21 | #include 22 | #include 23 | 24 | /// Provides bit level access to an array 25 | class BitArray { 26 | public: 27 | BitArray(const uint8_t *data, size_t size = SIZE_MAX) : d_data(data), d_size(size * 8) {} 28 | 29 | /// Get `n` bits 30 | uint32_t peek(size_t n) { 31 | if (d_pos + n > d_size) { 32 | throw std::out_of_range("BitArray: read past array boundary"); 33 | } 34 | uint32_t result = 0; 35 | for (size_t i = 0; i < n; i++) { 36 | size_t x = d_pos + i; 37 | 38 | bool bit = std::bitset<8>(d_data[x / 8]).test(7 - x % 8); 39 | result = result << 1 | bit; 40 | } 41 | return result; 42 | } 43 | 44 | /// Seek `n` bits forward 45 | void advance(size_t n) { d_pos += n; } 46 | 47 | /// Get `n` bits and see forward 48 | uint32_t fetch(size_t n) { 49 | uint32_t result = peek(n); 50 | advance(n); 51 | return result; 52 | } 53 | 54 | private: 55 | const uint8_t *d_data; 56 | const size_t d_size; 57 | size_t d_pos = 0; 58 | }; 59 | 60 | // clang-format off 61 | const uint8_t dc_category_len[12] = { 62 | 2, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9 63 | }; 64 | const uint8_t ac_table_size[17] = { 65 | 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125 66 | }; 67 | const uint8_t ac_table[] = { 68 | 1, 2, 69 | 3, 70 | 0, 4, 17, 71 | 5, 18, 33, 72 | 49, 65, 73 | 6, 19, 81, 97, 74 | 7, 34, 113, 75 | 20, 50, 129, 145, 161, 76 | 8, 35, 66, 177, 193, 77 | 21, 82, 209, 240, 78 | 36, 51, 98, 114, 79 | 130, 80 | 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55, 56, 57, 81 | 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 82 | 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 131, 83 | 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, 152, 153, 84 | 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 85 | 183, 184, 185, 186, 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 86 | 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, 230, 231, 232, 87 | 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250 88 | }; 89 | // clang-format on 90 | 91 | static int16_t apply_sign(uint16_t x, uint8_t category) { 92 | int16_t max = (1 << category) - 1; 93 | 94 | if (std::bitset<16>(x)[category - 1]) { 95 | return x; 96 | } else { 97 | return (int16_t)x - max; 98 | } 99 | } 100 | 101 | static int get_dc_category(uint16_t word) { 102 | if (word >> 14 == 0x0) return 0; 103 | switch (word >> 13) { 104 | case 0x2: 105 | return 1; 106 | case 0x3: 107 | return 2; 108 | case 0x4: 109 | return 3; 110 | case 0x5: 111 | return 4; 112 | case 0x6: 113 | return 5; 114 | } 115 | if (word >> 12 == 0xE) return 6; 116 | if (word >> 11 == 0x1E) return 7; 117 | if (word >> 10 == 0x3E) return 8; 118 | if (word >> 9 == 0x7E) return 9; 119 | if (word >> 8 == 0xFE) return 10; 120 | if (word >> 7 == 0x1FE) return 11; 121 | return -1; 122 | } 123 | 124 | #define DECOMPRESS(x) apply_sign(b.fetch(x), x) 125 | 126 | // TODO: exit if reading past the boundary of `in` 127 | bool huffman_decode(const uint8_t *in, std::array, MCU_PER_PACKET> &out, size_t n, size_t size) { 128 | BitArray b(in, size); 129 | int16_t dc = 0; 130 | 131 | for (size_t i = 0; i < n; i++) { 132 | // Extract the DC category 133 | int dc_category = get_dc_category(b.peek(16)); 134 | if (dc_category == -1) return false; 135 | b.advance(dc_category_len[dc_category]); 136 | 137 | // Decompress the DC coefficient 138 | dc += DECOMPRESS(dc_category); 139 | out[i][0] = dc; 140 | 141 | // Decompress the AC coefficients 142 | for (size_t j = 1; j < 64; j++) { 143 | uint8_t ac_len = 1; 144 | uint8_t ac_run = 0; 145 | uint8_t ac_category = 0; 146 | 147 | // Extract the AC code 148 | // This is adapted from https://github.com/dbdexter-dev/meteor_decode/blob/master/jpeg/huffman.c#L89-L100 149 | { 150 | uint16_t first_coeff = 0; 151 | size_t ac_idx = 0; 152 | uint32_t ac_buf = b.peek(32); 153 | for (; ac_len < 17; ac_len++) { 154 | uint32_t word = ac_buf >> (32 - ac_len); 155 | 156 | // If the coefficient belongs to this range, decompress it 157 | if (word - first_coeff < ac_table_size[ac_len]) { 158 | uint8_t ac_info = ac_table[ac_idx + word - first_coeff]; 159 | ac_run = ac_info >> 4; 160 | ac_category = ac_info & 0x0F; 161 | break; 162 | } 163 | 164 | first_coeff = (first_coeff + ac_table_size[ac_len]) << 1; 165 | ac_idx += ac_table_size[ac_len]; 166 | } 167 | } 168 | 169 | b.advance(ac_len); 170 | 171 | if (ac_run == 0 && ac_category == 0) { 172 | // Fill the rest of this block 173 | for (; j < 64; j++) { 174 | out[i][j] = 0; 175 | } 176 | } else { 177 | // Sanity check 178 | if (j + ac_run >= 64) return false; 179 | 180 | // The actual decompression 181 | for (size_t x = 0; x < ac_run; x++) { 182 | out[i][j++] = 0; 183 | } 184 | out[i][j] = DECOMPRESS(ac_category); 185 | } 186 | } 187 | } 188 | 189 | return true; 190 | } 191 | -------------------------------------------------------------------------------- /src/protocol/lrpt/huffman.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROTOCOL_LRPT_HUFFMAN_H_ 20 | #define LEANHRPT_PROTOCOL_LRPT_HUFFMAN_H_ 21 | 22 | #include 23 | 24 | #include "packet.h" 25 | 26 | bool huffman_decode(const uint8_t *in, std::array, MCU_PER_PACKET> &out, size_t n, size_t size); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/protocol/lrpt/jpeg.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "jpeg.h" 20 | 21 | #include 22 | #include 23 | 24 | namespace jpeg { 25 | // clang-format off 26 | /// IJG standard (Q=50) quantization table 27 | const int jpeg_qtable[8][8] = { 28 | { 16, 11, 10, 16, 24, 40, 51, 61 }, 29 | { 12, 12, 14, 19, 26, 58, 60, 55 }, 30 | { 14, 13, 16, 24, 40, 57, 69, 56 }, 31 | { 14, 17, 22, 29, 51, 87, 80, 62 }, 32 | { 18, 22, 37, 56, 68, 109, 103, 77 }, 33 | { 24, 35, 55, 64, 81, 104, 113, 92 }, 34 | { 49, 64, 78, 87, 103, 121, 120, 101 }, 35 | { 72, 92, 95, 98, 112, 100, 103, 99 } 36 | }; 37 | 38 | /// 8x8 JPEG zigzag pattern 39 | const int jpeg_zigzag[8][8] = { 40 | { 0, 1, 5, 6, 14, 15, 27, 28 }, 41 | { 2, 4, 7, 13, 16, 26, 29, 42 }, 42 | { 3, 8, 12, 17, 25, 30, 41, 43 }, 43 | { 9, 11, 18, 24, 31, 40, 44, 53 }, 44 | { 10, 19, 23, 32, 39, 45, 52, 54 }, 45 | { 20, 22, 33, 38, 46, 51, 55, 60 }, 46 | { 21, 34, 37, 47, 50, 56, 59, 61 }, 47 | { 35, 36, 48, 49, 57, 58, 62, 63 } 48 | }; 49 | 50 | /** 51 | * Look up table for values of cosine needed for the 8x8 IDCT, generated with: 52 | * 53 | * ``` 54 | * void init_cosine_lut() { 55 | * for (size_t y = 0; y < 8; y++) { 56 | * for (size_t x = 0; x < 8; x++) { 57 | * // https://en.wikipedia.org/wiki/JPEG#Decoding 58 | * cosine_lut[y][x] = cosf(((2.0f*y + 1.0f) * x * M_PI) / 16.0f); 59 | * } 60 | * 61 | * // Normalizing factor 62 | * cosine_lut[y][0] /= sqrtf(2.0); 63 | * } 64 | * } 65 | * ``` 66 | */ 67 | const float cosine_lut[8][8] = { 68 | { 0.707107f, 0.980785f, 0.92388f, 0.83147f, 0.707107f, 0.55557f, 0.382683f, 0.19509f }, 69 | { 0.707107f, 0.83147f, 0.382683f, -0.19509f, -0.707107f, -0.980785f, -0.92388f, -0.55557f, }, 70 | { 0.707107f, 0.55557f, -0.382683f, -0.980785f, -0.707107f, 0.19509f, 0.92388f, 0.83147f }, 71 | { 0.707107f, 0.19509f, -0.92388f, -0.55557f, 0.707107f, 0.83147f, -0.382683f, -0.980785f }, 72 | { 0.707107f, -0.19509f, -0.92388f, 0.55557f, 0.707107f, -0.83147f, -0.382684f, 0.980785f }, 73 | { 0.707107f, -0.55557f, -0.382684f, 0.980785f, -0.707107f, -0.19509f, 0.92388f, -0.83147f }, 74 | { 0.707107f, -0.83147f, 0.382684f, 0.195091f, -0.707107f, 0.980785f, -0.923879f, 0.55557f }, 75 | { 0.707107f, -0.980785f, 0.92388f, -0.83147f, 0.707107f, -0.55557f, 0.382684f, -0.19509f } 76 | }; 77 | // clang-format on 78 | 79 | static void unzigzag(const std::array &in, jpeg::block &out) { 80 | for (size_t y = 0; y < 8; y++) { 81 | for (size_t x = 0; x < 8; x++) { 82 | int pos = jpeg_zigzag[y][x]; 83 | out[y][x] = in[pos]; 84 | } 85 | } 86 | } 87 | 88 | // See https://web.archive.org/web/20150223223556/http://meteor.robonuka.ru/for-experts/soft/ 89 | static int qfactor(size_t x, size_t y, uint8_t q) { 90 | float f; 91 | if (q < 50) { 92 | f = 5000.0f / (float)q; 93 | } else { 94 | f = 200.0f - 2.0f * (float)q; 95 | } 96 | 97 | int ptk = roundf(f / 100.0f * (float)jpeg_qtable[y][x]); 98 | if (ptk > 1) { 99 | return ptk; 100 | } else { 101 | return 1; 102 | } 103 | } 104 | 105 | static void dequantize(jpeg::block &block, uint8_t q) { 106 | for (size_t y = 0; y < 8; y++) { 107 | for (size_t x = 0; x < 8; x++) { 108 | block[y][x] *= qfactor(x, y, q); 109 | } 110 | } 111 | } 112 | 113 | /// 2D 8x8 inverse DCT 114 | static void idct(const jpeg::block &in, jpeg::block &out) { 115 | for (size_t y = 0; y < 8; y++) { 116 | for (size_t x = 0; x < 8; x++) { 117 | float sum = 0.0; 118 | 119 | for (size_t i = 0; i < 8; i++) { 120 | for (size_t j = 0; j < 8; j++) { 121 | sum += in[j][i] * cosine_lut[x][i] * cosine_lut[y][j]; 122 | } 123 | } 124 | 125 | sum = sum / 4.0f + 128.0f; 126 | out[y][x] = std::min(std::max(sum, 0.0f), 255.0f); 127 | } 128 | } 129 | } 130 | 131 | void decode_block(const std::array &in, jpeg::block &out, uint8_t q) { 132 | if (q < 20 || q > 100) { 133 | return; 134 | } 135 | 136 | jpeg::block tmp; 137 | unzigzag(in, tmp); 138 | dequantize(tmp, q); 139 | idct(tmp, out); 140 | } 141 | } // namespace jpeg 142 | -------------------------------------------------------------------------------- /src/protocol/lrpt/jpeg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROTOCOL_LRPT_JPEG_H_ 20 | #define LEANHRPT_PROTOCOL_LRPT_JPEG_H_ 21 | 22 | #include 23 | #include 24 | 25 | namespace jpeg { 26 | template 27 | using block = std::array, 8>; 28 | 29 | void decode_block(const std::array &in, jpeg::block &out, uint8_t q); 30 | } // namespace jpeg 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/protocol/lrpt/packet.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "packet.h" 20 | 21 | #include 22 | #include 23 | 24 | #include "huffman.h" 25 | 26 | namespace lrpt { 27 | bool decode_packet(std::array, MCU_PER_PACKET> &out, const uint8_t *data, uint8_t q, size_t n) { 28 | if (n == 0) { 29 | return false; 30 | } 31 | 32 | // Huffman decompression 33 | std::array, MCU_PER_PACKET> decompressed; 34 | try { 35 | if (!huffman_decode(data, decompressed, MCU_PER_PACKET, n)) { 36 | return false; 37 | } 38 | } catch (std::out_of_range &e) { 39 | return false; 40 | } 41 | 42 | // Decode the blocks 43 | for (size_t i = 0; i < MCU_PER_PACKET; i++) { 44 | jpeg::decode_block(decompressed[i], out[i], q); 45 | } 46 | 47 | return true; 48 | } 49 | } // namespace lrpt 50 | -------------------------------------------------------------------------------- /src/protocol/lrpt/packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROTOCOL_LRPT_PACKET_H_ 20 | #define LEANHRPT_PROTOCOL_LRPT_PACKET_H_ 21 | 22 | #include 23 | #include 24 | 25 | #include "jpeg.h" 26 | 27 | #define MCU_PER_PACKET 14 28 | 29 | namespace lrpt { 30 | bool decode_packet(std::array, MCU_PER_PACKET> &out, const uint8_t *data, uint8_t q, size_t n); 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/protocol/repack.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "repack.h" 20 | 21 | #include 22 | 23 | template 24 | void arbitrary_repack(const uint8_t *in, T *out, size_t n) { 25 | size_t pos = 0; 26 | for (size_t i = 0; i < n; i++) { 27 | out[i] = 0; 28 | for (size_t j = pos; j < pos + N; j++) { 29 | bool bit = std::bitset<8>(in[j / 8]).test(7 - j % 8); 30 | out[i] = out[i] << 1 | bit; 31 | } 32 | pos += N; 33 | } 34 | } 35 | 36 | // HIRS 37 | template void arbitrary_repack(const uint8_t *in, uint16_t *out, size_t n); 38 | 39 | void repack10(const uint8_t *in, uint16_t *out, size_t n) { 40 | size_t j = 0; 41 | for (size_t i = 0; i < n; i += 4) { 42 | // clang-format off 43 | out[i + 0] = (in[j + 0] << 2) | (in[j + 1] >> 6); 44 | out[i + 1] = ((in[j + 1] % 64) << 4) | (in[j + 2] >> 4); 45 | out[i + 2] = ((in[j + 2] % 16) << 6) | (in[j + 3] >> 2); 46 | out[i + 3] = ((in[j + 3] % 4 ) << 8) | in[j + 4]; 47 | j += 5; 48 | // clang-format on 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/protocol/repack.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROTOCOL_REPACK_H_ 20 | #define LEANHRPT_PROTOCOL_REPACK_H_ 21 | 22 | #include 23 | #include 24 | 25 | template 26 | void arbitrary_repack(const uint8_t *in, T *out, size_t n); 27 | 28 | void repack10(const uint8_t *in, uint16_t *out, size_t n); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/protocol/reverse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROTOCOL_REVERSE_H_ 20 | #define LEANHRPT_PROTOCOL_REVERSE_H_ 21 | 22 | #include 23 | 24 | // Taken from https://stackoverflow.com/a/2602885 25 | static uint8_t reverse_bits(uint8_t b) { 26 | b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; 27 | b = (b & 0xCC) >> 2 | (b & 0x33) << 2; 28 | b = (b & 0xAA) >> 1 | (b & 0x55) << 1; 29 | return b; 30 | } 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/protocol/timestamp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_PROTOCOL_TIMESTAMP_H_ 20 | #define LEANHRPT_PROTOCOL_TIMESTAMP_H_ 21 | 22 | #include 23 | 24 | inline std::vector filter_timestamps(std::vector in) { 25 | if (in.size() < 20) { 26 | return {}; 27 | } 28 | 29 | // Main filtering 30 | std::vector copy = in; 31 | for (size_t i = 9; i < in.size() - 9; i++) { 32 | double average = 0.0; 33 | for (int j = 0; j < 19; j++) { 34 | average += copy[i + (j - 9)]; 35 | } 36 | average /= 19.0; 37 | 38 | if (fabs(copy[i] - average) > 2.0) { 39 | in[i] = 0; 40 | } 41 | } 42 | 43 | // Remove the first and last 9 readings because these aren't filtered above 44 | for (size_t i = 0; i < 9; i++) { 45 | in[i] = 0; 46 | } 47 | for (size_t i = in.size() - 9; i < in.size(); i++) { 48 | in[i] = 0; 49 | } 50 | 51 | // Find first and last valid timestamp 52 | int first = -1, last = -1; 53 | for (size_t i = 0; i < in.size(); i++) { 54 | if (in[i] != 0.0 && first == -1) { 55 | first = i; 56 | } 57 | if (in[i] != 0.0) { 58 | last = i; 59 | } 60 | } 61 | 62 | // No valid timestamps 63 | if (first < 0 || last < 0) { 64 | return {}; 65 | } 66 | 67 | // Average seconds per line over the pass 68 | double spl = (in[last] - in[first]) / (last - first); 69 | 70 | // Fill in missing timestamps 71 | for (size_t i = 1; i < in.size(); i++) { 72 | if (in[i] == 0.0 && in[i - 1] != 0.0) { 73 | in[i] = in[i - 1] + spl; 74 | } 75 | } 76 | for (int i = in.size() - 2; i >= 0; i--) { 77 | if (in[i] == 0.0 && in[i + 1] != 0.0) { 78 | in[i] = in[i + 1] - spl; 79 | } 80 | } 81 | 82 | return in; 83 | } 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /src/qt/projectdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ProjectDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 970 10 | 444 11 | 12 | 13 | 14 | Projector 15 | 16 | 17 | 18 | logo128.icologo128.ico 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 0 31 | 0 32 | 33 | 34 | 35 | Settings 36 | 37 | 38 | 39 | 40 | 41 | Bounds 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 0 50 | 0 51 | 52 | 53 | 54 | 55 | Auto 56 | 57 | 58 | 59 | 60 | Whole Earth 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | Resolution 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 0 77 | 0 78 | 79 | 80 | 81 | km 82 | 83 | 84 | 1 85 | 86 | 87 | 1.000000000000000 88 | 89 | 90 | 40.000000000000000 91 | 92 | 93 | 94 | 95 | 96 | 97 | Qt::Vertical 98 | 99 | 100 | 101 | 20 102 | 40 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | Click preview to calculate 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 0 119 | 0 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | Projection 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | Preview 140 | 141 | 142 | 143 | 144 | 145 | 146 | Render 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /src/qt/qpaletteview.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_QT_QPALETTEVIEW 20 | #define LEANHRPT_QT_QPALETTEVIEW 21 | 22 | #include 23 | #include 24 | 25 | #include "util.h" 26 | 27 | class QPaletteView : public QWidget { 28 | public: 29 | QPaletteView([[maybe_unused]] QWidget* parent) {} 30 | std::vector stops; 31 | 32 | private: 33 | virtual void paintEvent([[maybe_unused]] QPaintEvent* p) override { 34 | QPainter painter(this); 35 | painter.fillRect(0, 0, width(), height(), QColor(0, 0, 0)); 36 | if (stops.size() == 0) { 37 | stops.push_back(Qt::black); 38 | stops.push_back(Qt::white); 39 | }; 40 | 41 | for (size_t x = 0; x < (size_t)width(); x++) { 42 | double i = (double)x / (double)(width() - 1) * (stops.size() - 1); 43 | QColor color = lerp(stops[floor(i)], stops[ceil(i)], fmod(i, 1.0)); 44 | 45 | painter.setPen(color); 46 | painter.drawLine(x, 0, x, height()); 47 | } 48 | } 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/satinfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef LEANHRPT_SATINFO_H_ 20 | #define LEANHRPT_SATINFO_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | enum class Protocol { Unknown, LRPT, HRPT, AHRPT, MeteorHRPT, FengYunHRPT, GAC, GACReverse, DSB }; 27 | 28 | enum class Mission { POES, MeteorM, FengYun3, MetOp }; 29 | 30 | enum class SatID { 31 | Unknown = 0, 32 | MetOpA = 29499, 33 | MetOpB = 38771, 34 | MetOpC = 43689, 35 | FengYun3A = 32958, 36 | FengYun3B = 37214, 37 | FengYun3C = 39260, 38 | NOAA15 = 25338, 39 | NOAA18 = 28654, 40 | NOAA19 = 33591, 41 | MeteorM2 = 40069, 42 | MeteorM22 = 44387 43 | }; 44 | 45 | enum class Imager { AVHRR, VIRR, MSUMR, MHS, MTVZA, HIRS, AMSUA }; 46 | 47 | struct SensorInfo { 48 | std::string name; 49 | float swath; /// in km 50 | float resolution; /// in km/px 51 | size_t width; 52 | }; 53 | 54 | struct SatelliteInfo { 55 | float orbit_height; /// in km 56 | Mission mission; 57 | std::string name; 58 | Imager default_imager; 59 | }; 60 | 61 | // clang-format off 62 | const std::map sensor_info = { 63 | { Imager::AVHRR, SensorInfo {"AVHRR", 2900.0f, 1.1f, 2048 } }, 64 | { Imager::VIRR, SensorInfo {"VIRR", 2800.0f, 1.1f, 2048 } }, 65 | { Imager::MSUMR, SensorInfo {"MSU-MR", 2800.0f, 1.0f, 1572 } }, 66 | { Imager::MHS, SensorInfo {"MHS", 2180.0f, 16.0f, 90 } }, 67 | { Imager::MTVZA, SensorInfo {"MTVZA", 1500.0f, 4.0f, 200 } }, 68 | { Imager::HIRS, SensorInfo {"HIRS", 2160.0f, 40.0f, 56 } }, 69 | { Imager::AMSUA, SensorInfo {"AMSU-A", 2343.0f, 78.0f, 30 } }, 70 | }; 71 | 72 | inline Imager get_sensor(std::string name) { 73 | for (const auto &x : sensor_info) { 74 | if (x.second.name == name) { 75 | return x.first; 76 | } 77 | } 78 | 79 | throw std::invalid_argument("get_sensor called with an unknown sensor name"); 80 | } 81 | 82 | const std::map satellite_info { 83 | { SatID::MetOpA, SatelliteInfo { 827.0f, Mission::MetOp, "MetOp-A", Imager::AVHRR } }, 84 | { SatID::MetOpB, SatelliteInfo { 827.0f, Mission::MetOp, "MetOp-B", Imager::AVHRR } }, 85 | { SatID::MetOpC, SatelliteInfo { 817.0f, Mission::MetOp, "MetOp-C", Imager::AVHRR } }, 86 | { SatID::FengYun3A, SatelliteInfo { 834.0f, Mission::FengYun3, "FengYun-3A", Imager::VIRR } }, 87 | { SatID::FengYun3B, SatelliteInfo { 836.0f, Mission::FengYun3, "FengYun-3B", Imager::VIRR } }, 88 | { SatID::FengYun3C, SatelliteInfo { 836.0f, Mission::FengYun3, "FengYun-3C", Imager::VIRR } }, 89 | { SatID::NOAA15, SatelliteInfo { 813.0f, Mission::POES, "NOAA-15", Imager::AVHRR } }, 90 | { SatID::NOAA18, SatelliteInfo { 854.0f, Mission::POES, "NOAA-18", Imager::AVHRR } }, 91 | { SatID::NOAA19, SatelliteInfo { 870.0f, Mission::POES, "NOAA-19", Imager::AVHRR } }, 92 | { SatID::MeteorM2, SatelliteInfo { 820.0f, Mission::MeteorM, "Meteor-M2", Imager::MSUMR } }, 93 | { SatID::MeteorM22, SatelliteInfo { 821.0f, Mission::MeteorM, "Meteor-M22", Imager::MSUMR } }, 94 | }; 95 | // clang-format on 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef M_PI 20 | #define M_PI 3.14159265358979323846 21 | #endif 22 | #ifndef M_2PI 23 | #define M_2PI (M_PI * 2.0) 24 | #endif 25 | #ifndef M_PI_2 26 | #define M_PI_2 (M_PI / 2.0) 27 | #endif 28 | #ifndef M_PI_4 29 | #define M_PI_4 (M_PI / 2.0) 30 | #endif 31 | 32 | #ifndef LEANHRPT_UTIL_H_ 33 | #define LEANHRPT_UTIL_H_ 34 | 35 | #include 36 | 37 | #define RAD2DEG (180.0 / M_PI) 38 | #define DEG2RAD (M_PI / 180.0) 39 | 40 | inline double deg2rad(double deg) { return deg * DEG2RAD; } 41 | inline double rad2deg(double rad) { return rad * RAD2DEG; } 42 | #ifdef QPOINT_H 43 | inline QPointF deg2rad(QPointF deg) { return QPointF(deg.x() * DEG2RAD, deg.y() * DEG2RAD); } 44 | inline QPointF rad2deg(QPointF rad) { return QPointF(rad.x() * RAD2DEG, rad.y() * RAD2DEG); } 45 | #endif 46 | 47 | template 48 | inline T clamp(T v, T lo, T hi) { 49 | return std::max(lo, std::min(hi, v)); 50 | } 51 | 52 | template 53 | inline T lerp(T a, T b, T x) { 54 | return a * (1.0 - x) + b * x; 55 | } 56 | 57 | #ifdef QCOLOR_H 58 | // clang-format off 59 | inline QRgba64 lerp(QRgba64 a, QRgba64 b, double x) { 60 | return QRgba64::fromRgba64( 61 | a.red() * (1.0 - x) + b.red() * x, 62 | a.green() * (1.0 - x) + b.green() * x, 63 | a.blue() * (1.0 - x) + b.blue() * x, 64 | a.alpha() * (1.0 - x) + b.alpha() * x 65 | ); 66 | } 67 | 68 | inline QColor lerp(QColor a, QColor b, double x) { 69 | return QColor::fromRgbF( 70 | a.redF() * (1.0 - x) + b.redF() * x, 71 | a.greenF() * (1.0 - x) + b.greenF() * x, 72 | a.blueF() * (1.0 - x) + b.blueF() * x, 73 | a.alphaF() * (1.0 - x) + b.alphaF() * x 74 | ); 75 | } 76 | // clang-format on 77 | #endif 78 | 79 | #if defined(QIMAGE_H) && defined(QCOLOR_H) 80 | // clang-format off 81 | inline QColor lerp2(const QImage &image, double x, double y) { 82 | QColor a = lerp(image.pixelColor(floor(x), floor(y)), image.pixelColor(ceil(x), floor(y)), fmod(x, 1.0)); 83 | QColor b = lerp(image.pixelColor(floor(x), ceil(y)), image.pixelColor(ceil(x), ceil(y)), fmod(x, 1.0)); 84 | 85 | QColor c = lerp(a, b, fmod(y, 1.0)); 86 | if (c.alphaF() != 1.0) { 87 | return Qt::transparent; 88 | } else { 89 | return c; 90 | } 91 | } 92 | // clang-format on 93 | 94 | inline QColor lerp2(const QImage &image, QPointF point) { return lerp2(image, point.x(), point.y()); } 95 | #endif 96 | 97 | template 98 | void reverse(T &array) { 99 | for (size_t i = 0; i < array.size() / 2; i++) { 100 | std::swap(array[i], array[(array.size() - 1) - i]); 101 | } 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | project(LeanHRPT-Tools CXX) 3 | 4 | add_executable(vcdu2cadu vcdu2cadu.cpp) 5 | 6 | add_executable(bin2raw16 bin2raw16.cpp ${CMAKE_SOURCE_DIR}/src/protocol/deframer.cpp ${CMAKE_SOURCE_DIR}/src/protocol/repack.cpp) 7 | target_include_directories(bin2raw16 PUBLIC ${CMAKE_SOURCE_DIR}/src) 8 | 9 | add_executable(bin2cadu bin2cadu.cpp ${CMAKE_SOURCE_DIR}/src/protocol/ccsds/deframer.cpp) 10 | target_include_directories(bin2cadu PUBLIC ${CMAKE_SOURCE_DIR}/src) 11 | -------------------------------------------------------------------------------- /tools/bin2cadu.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "cli.h" 20 | #include "protocol/ccsds/deframer.h" 21 | 22 | int main(int argc, char *argv[]) { 23 | std::ifstream in; 24 | std::ofstream out; 25 | interface("input.bin", "output.cadu", in, out, argc, argv); 26 | 27 | ccsds::Deframer deframer; 28 | uint8_t buffer[1024]; 29 | uint8_t frame[1024]; 30 | while (!in.eof()) { 31 | in.read((char *)buffer, 1024); 32 | 33 | if (deframer.work(buffer, frame, 1024)) { 34 | out.write((char *)frame, 1024); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tools/bin2raw16.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "cli.h" 20 | #include "protocol/deframer.h" 21 | #include "protocol/repack.h" 22 | 23 | int main(int argc, char *argv[]) { 24 | std::ifstream in; 25 | std::ofstream out; 26 | interface("input.bin", "output.raw16", in, out, argc, argv); 27 | 28 | ArbitraryDeframer deframer(8, true); 29 | uint8_t buffer[1024]; 30 | uint8_t frame[13863]; 31 | uint16_t repacked[11090]; 32 | while (!in.eof()) { 33 | in.read((char *)buffer, 1024); 34 | 35 | if (deframer.work(buffer, frame, 1024)) { 36 | repack10(frame, repacked, 11090); 37 | out.write((char *)repacked, 11090 * sizeof(uint16_t)); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tools/cli.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void interface(std::string in_filename, std::string out_filename, std::ifstream &in, std::ofstream &out, int argc, 5 | char *argv[]) { 6 | if (argc != 3) { 7 | std::cout << "Usage: " << argv[0] << " " << in_filename << " " << out_filename << std::endl; 8 | exit(1); 9 | } 10 | 11 | in = std::ifstream(argv[1], std::ios::binary); 12 | if (!in.is_open()) { 13 | std::cout << "Could not open input file" << std::endl; 14 | exit(1); 15 | } 16 | 17 | out = std::ofstream(argv[2], std::ios::binary); 18 | if (!out.is_open()) { 19 | std::cout << "Could not open output file" << std::endl; 20 | exit(1); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tools/vcdu2cadu.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * LeanHRPT Decode 3 | * Copyright (C) 2021-2022 Xerbo 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "cli.h" 20 | 21 | int main(int argc, char *argv[]) { 22 | std::ifstream in; 23 | std::ofstream out; 24 | interface("input.vcdu", "output.cadu", in, out, argc, argv); 25 | 26 | uint8_t frame[1024] = {0x1A, 0xCF, 0xFC, 0x1D}; 27 | while (!in.eof()) { 28 | in.read((char *)&frame[4], 892); 29 | out.write((char *)frame, 1024); 30 | } 31 | } 32 | --------------------------------------------------------------------------------