Auteur | SHA1 | Bericht | Datum |
---|---|---|---|
Xerbo |
66b8dd026e
|
Fix memory leak and move state entirely into aptdec_t | 1 jaar geleden |
Xerbo |
071a9c8f84
|
Better naming, but valid this time | 1 jaar geleden |
Xerbo |
c9d89ea210
|
Better naming | 1 jaar geleden |
Xerbo |
61ec605ffd
|
Fix syntax | 1 jaar geleden |
Xerbo |
8c6cba0aa3
|
ARM builds | 1 jaar geleden |
Xerbo |
711d0e0163
|
MSVC build in GitHub actions | 1 jaar geleden |
Xerbo |
c551e70335
|
Include missing header and fix stddev
Standard deviation on a non complete population sample is divided by n-1, not n |
1 jaar geleden |
@@ -4,7 +4,6 @@ about: Create a bug report to improve aptdec | |||
title: '' | |||
labels: bug | |||
assignees: '' | |||
--- | |||
**Describe the bug** | |||
@@ -17,7 +16,7 @@ Steps to reproduce the behavior. | |||
A clear description of what you expected to happen. | |||
**Link to audio** | |||
A link to the audio that caused the problem. | |||
A link to the audio file that caused the problem (if applicable). | |||
**Build information** | |||
The commit of aptdec you are running (check with `git rev-parse HEAD`), make sure it's up to date before opening this issue. | |||
The version of aptdec you are running (check with `git describe --tag`), make sure it's up to date before opening this issue. |
@@ -29,16 +29,16 @@ jobs: | |||
- name: Build and package | |||
run: cmake --build build -j$(nproc) && cmake --build build --target package | |||
- name: Upload TGZ package | |||
- name: Upload TGZ archive | |||
uses: actions/upload-artifact@v3 | |||
with: | |||
name: TGZ package | |||
name: TGZ archive (x86_64-gcc) | |||
path: build/aptdec_*.tar.gz | |||
- name: Upload DEB package | |||
- name: Upload Debian package | |||
uses: actions/upload-artifact@v3 | |||
with: | |||
name: Debian package | |||
name: Debian package (x86_64-gcc) | |||
path: build/aptdec_*.deb | |||
build_windows: | |||
@@ -56,8 +56,51 @@ jobs: | |||
- name: Run build script | |||
run: ./build_windows.sh $BUILD_TYPE | |||
- name: Upload ZIP package | |||
- name: Upload ZIP archive | |||
uses: actions/upload-artifact@v3 | |||
with: | |||
name: ZIP package | |||
name: ZIP archive (x86_64-MinGW) | |||
path: winbuild/aptdec_*.zip | |||
build_windows_msvc: | |||
runs-on: windows-latest | |||
steps: | |||
- uses: actions/checkout@v3 | |||
with: | |||
submodules: 'recursive' | |||
fetch-depth: 0 | |||
- name: Run build script | |||
shell: cmd | |||
run: '"C:/Program Files/Microsoft Visual Studio/2022/Enterprise/VC/Auxiliary/Build/vcvars64.bat" & build_windows.bat' | |||
- name: Upload ZIP archive | |||
uses: actions/upload-artifact@v3 | |||
with: | |||
name: ZIP archive (x86_64-MSVC) | |||
path: winbuild/aptdec_*.zip | |||
build_linux_armhf: | |||
runs-on: ubuntu-latest | |||
steps: | |||
- uses: actions/checkout@v3 | |||
with: | |||
submodules: 'recursive' | |||
fetch-depth: 0 | |||
- name: Run build script | |||
run: docker run -v $(pwd):/aptdec:z -w /aptdec debian:11 ./build_arm.sh | |||
- name: Upload Debian package | |||
uses: actions/upload-artifact@v3 | |||
with: | |||
name: Debian package (armhf-gcc) | |||
path: build/aptdec_*.deb | |||
- name: Upload TGZ archive | |||
uses: actions/upload-artifact@v3 | |||
with: | |||
name: TGZ archive (armhf-gcc) | |||
path: build/aptdec_*.tar.gz |
@@ -89,3 +89,4 @@ zlib/ | |||
libpng/ | |||
libsndfile-*-win64.zip | |||
libsndfile-*-win64/ | |||
root/ |
@@ -79,13 +79,14 @@ endif() | |||
install(TARGETS aptdec PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/aptdec LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) | |||
# Packaging | |||
set(CPACK_PACKAGE_VERSION "${VERSION}") | |||
string(REPLACE v "" CPACK_PACKAGE_VERSION ${VERSION}) | |||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) | |||
set(CPACK_PACKAGE_NAME "aptdec") | |||
set(CPACK_PACKAGE_CONTACT "Xerbo (xerbo@protonmail.com)") | |||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "NOAA APT satellite imagery decoder") | |||
set(CPACK_PACKAGE_DESCRIPTION "Aptdec is a FOSS library/program that decodes images transmitted by the NOAA POES weather satellites. These satellites transmit constantly (among other things) medium resolution (4km/px) images of the earth over a analog mode called APT.") | |||
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}_${CPACK_PACKAGE_VERSION}.${CMAKE_SYSTEM_PROCESSOR}") | |||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) | |||
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libsndfile1, libpng16-16") | |||
if(WIN32) | |||
file(GLOB DLLS ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/*.dll) | |||
@@ -0,0 +1,34 @@ | |||
# Upgrading from aptdec 1.8.0 to 2.0.0 | |||
# `aptdec` (now `aptdec-cli`) | |||
## Removed | |||
- The `-d` (output directory) flag | |||
- The `-g` (gamma) flag | |||
- Map overlays/MCIR | |||
- Reading raw PNG images | |||
(Some of these features have been moved into `aptdec_post.py`) | |||
## Changes | |||
- "palettes" are now called "luts" | |||
- Image type and effects are now full English words (separated by commas) instead of letters | |||
- Output format of realtime images is now `CURRENT_TIME.png` instead of `CURRENT_TIME-r.png` | |||
- Channel A/B images include telemetry | |||
## Examples | |||
| v1.8 | v2.0.0 | | |||
|---------------------------------------|-------------------------------------------| | |||
| `aptdec file.wav` | `aptdec-cli file.wav` | | |||
| `aptdec -i rt file.wav` | `aptdec-cli -i raw,thermal file.wav` | | |||
| `aptdec -i ab file.wav` | `aptdec-cli -i a,b -e strip file.wav` | | |||
| `aptdec -i p -p palette.png file.wav` | `aptdec-cli -i lut -l lut.png file.wav` | | |||
| `aptdec -d out *.wav` | `mkdir out; cd out; aptdec-cli ../*.wav` | | |||
# `libapt` (now `libaptdec`) | |||
To avoid confusion with `apt` (the Debian package manager) the name of the aptdec library has been changed to `libaptdec`. |
@@ -244,6 +244,7 @@ static int process_file(const char *path, options_t *opts) { | |||
// Normalize | |||
int error; | |||
apt_image_t img = apt_normalize(data, rows, opts->satellite, &error); | |||
free(data); | |||
if (error) { | |||
error_noexit("Normalization failed"); | |||
return 0; | |||
@@ -0,0 +1,18 @@ | |||
#!/usr/bin/env bash | |||
# This script only works on Debian Bullseye | |||
# If you have docker this is trivially easy: | |||
# docker run -v $(pwd):/aptdec:z -w /aptdec debian:11 ./build_arm.sh | |||
apt-get update | |||
apt-get install -y debootstrap cmake gcc-arm-linux-gnueabihf | |||
# Prepare armhf root environment | |||
if [ ! -d root ]; then | |||
debootstrap --keyring=/usr/share/keyrings/debian-archive-keyring.gpg --arch=armhf --include=libsndfile1-dev,libpng-dev --download-only bullseye root http://deb.debian.org/debian/ | |||
for i in root/var/cache/apt/archives/*.deb; do dpkg -x "$i" root; done | |||
fi | |||
# Build aptdec | |||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-armhf.cmake -DCMAKE_INSTALL_PREFIX=/aptdec/root | |||
cmake --build build -j$(nproc) | |||
cmake --build build --target package |
@@ -1,36 +1,38 @@ | |||
REM Build using MSVC on Windows | |||
REM Requires: git, cmake and ninja | |||
REM You need to run vcvars before running this | |||
REM Build zlib | |||
IF NOT EXIST zlib ( | |||
git clone -b v1.2.13 https://github.com/madler/zlib | |||
git clone --depth 1 -b v1.2.13 https://github.com/madler/zlib | |||
cd zlib | |||
cmake -B build -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DMSVC_TOOLSET_VERSION=190 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../winpath | |||
cmake --build build -j4 | |||
cmake -B build -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../winpath | |||
cmake --build build -j%NUMBER_OF_PROCESSORS% | |||
cmake --build build --target install | |||
cd .. | |||
) | |||
REM Build libpng | |||
IF NOT EXIST libpng ( | |||
git clone -b v1.6.39 https://github.com/glennrp/libpng | |||
git clone --depth 1 -b v1.6.39 https://github.com/glennrp/libpng | |||
cd libpng | |||
cmake -B build -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DMSVC_TOOLSET_VERSION=190 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../winpath -DPNG_STATIC=OFF -DPNG_EXECUTABLES=OFF -DPNG_TESTS=OFF | |||
cmake --build build -j4 | |||
cmake -B build -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../winpath -DPNG_STATIC=OFF -DPNG_EXECUTABLES=OFF -DPNG_TESTS=OFF | |||
cmake --build build -j%NUMBER_OF_PROCESSORS% | |||
cmake --build build --target install | |||
cd .. | |||
) | |||
REM Build libsndfile, only with WAV support | |||
IF NOT EXIST libsndfile ( | |||
git clone -b 1.2.0 https://github.com/libsndfile/libsndfile | |||
git clone --depth 1 -b 1.2.0 https://github.com/libsndfile/libsndfile | |||
cd libsndfile | |||
cmake -B build -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DMSVC_TOOLSET_VERSION=190 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../winpath -BUILD_SHARED_LIBS=ON -DBUILD_EXAMPLES=OFF -DBUILD_PROGRAMS=OFF | |||
cmake --build build -j4 | |||
cmake -B build -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../winpath -DBUILD_SHARED_LIBS=ON -DBUILD_EXAMPLES=OFF -DBUILD_PROGRAMS=OFF | |||
cmake --build build -j%NUMBER_OF_PROCESSORS% | |||
cmake --build build --target install | |||
cd .. | |||
) | |||
REM Build aptdec | |||
cmake -B winbuild -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DMSVC_TOOLSET_VERSION=190 -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../winpath | |||
cmake --build winbuil -j4 | |||
cmake -B winbuild -G Ninja -DCMAKE_C_COMPILER="cl.exe" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../winpath | |||
cmake --build winbuild -j%NUMBER_OF_PROCESSORS% | |||
cmake --build winbuild --target package |
@@ -0,0 +1,19 @@ | |||
# the name of the target operating system | |||
SET(CMAKE_SYSTEM_NAME Linux) | |||
SET(CMAKE_SYSTEM_PROCESSOR armhf) | |||
# which compilers to use for C and C++ | |||
SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) | |||
SET(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) | |||
# here is the target environment located | |||
SET(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf/ ${CMAKE_INSTALL_PREFIX}) | |||
# Hack | |||
link_directories(${CMAKE_INSTALL_PREFIX}/lib/arm-linux-gnueabihf) | |||
# adjust the default behaviour of the FIND_XXX() commands: | |||
# search headers and libraries in the target environment, search | |||
# programs in the host environment | |||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) | |||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) | |||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) |
@@ -1,6 +1,6 @@ | |||
# the name of the target operating system | |||
SET(CMAKE_SYSTEM_NAME Windows) | |||
SET(CMAKE_SYSTEM_PROCESSOR amd64) | |||
SET(CMAKE_SYSTEM_PROCESSOR x86_64) | |||
# which compilers to use for C and C++ | |||
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) | |||
@@ -52,12 +52,9 @@ linear_t linear_regression(const float *independent, const float *dependent, siz | |||
return (linear_t){a, b}; | |||
} | |||
// "Sample" standard deviation | |||
float standard_deviation(const float *data, size_t len) { | |||
float mean = 0.0f; | |||
for (size_t i = 0; i < len; i++) { | |||
mean += data[i]; | |||
} | |||
mean /= (float)len; | |||
float mean = meanf(data, len); | |||
float deviation_mean = 0.0f; | |||
for (size_t i = 0; i < len; i++) { | |||
@@ -65,7 +62,7 @@ float standard_deviation(const float *data, size_t len) { | |||
deviation_mean += deviation * deviation; | |||
} | |||
return sqrtf(deviation_mean / (float)len); | |||
return sqrtf(deviation_mean / (float)(len-1)); | |||
} | |||
float sumf(const float *x, size_t len) { | |||
@@ -32,6 +32,10 @@ | |||
#define CARRIER_FREQ 2400.0f | |||
#define MAX_CARRIER_OFFSET 10.0f | |||
const float sync_pattern[] = {-1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, | |||
1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 0}; | |||
#define SYNC_SIZE (sizeof(sync_pattern)/sizeof(sync_pattern[0])) | |||
typedef struct { | |||
float alpha; | |||
float beta; | |||
@@ -58,6 +62,11 @@ struct aptdec_t { | |||
fir_t *hilbert; | |||
float low_pass[LOW_PASS_SIZE]; | |||
float row_buffer[APT_IMG_WIDTH + SYNC_SIZE + 2]; | |||
float interpolator_buffer[APTDEC_BUFFER_SIZE]; | |||
size_t interpolator_n; | |||
float interpolator_offset; | |||
}; | |||
char *aptdec_get_version(void) { | |||
@@ -162,47 +171,38 @@ static int am_demod(aptdec_t *apt, float *out, size_t count, aptdec_callback_t c | |||
} | |||
static int get_pixels(aptdec_t *apt, float *out, size_t count, aptdec_callback_t callback, void *context) { | |||
static float buffer[APTDEC_BUFFER_SIZE]; | |||
static size_t n = APTDEC_BUFFER_SIZE; | |||
static float offset = 0.0; | |||
float ratio = apt->sample_rate / (4160.0f * apt->sync_frequency); | |||
for (size_t i = 0; i < count; i++) { | |||
// Get more samples if there are less than `LOW_PASS_SIZE` available | |||
if (n + LOW_PASS_SIZE > APTDEC_BUFFER_SIZE) { | |||
memcpy(buffer, &buffer[n], (APTDEC_BUFFER_SIZE-n) * sizeof(float)); | |||
if (apt->interpolator_n + LOW_PASS_SIZE > APTDEC_BUFFER_SIZE) { | |||
memcpy(apt->interpolator_buffer, &apt->interpolator_buffer[apt->interpolator_n], (APTDEC_BUFFER_SIZE-apt->interpolator_n) * sizeof(float)); | |||
size_t read = am_demod(apt, &buffer[APTDEC_BUFFER_SIZE-n], n, callback, context); | |||
if (read != n) { | |||
size_t read = am_demod(apt, &apt->interpolator_buffer[APTDEC_BUFFER_SIZE-apt->interpolator_n], apt->interpolator_n, callback, context); | |||
if (read != apt->interpolator_n) { | |||
return i; | |||
} | |||
n = 0; | |||
apt->interpolator_n = 0; | |||
} | |||
out[i] = interpolating_convolve(&buffer[n], apt->low_pass, LOW_PASS_SIZE, offset); | |||
out[i] = interpolating_convolve(&apt->interpolator_buffer[apt->interpolator_n], apt->low_pass, LOW_PASS_SIZE, apt->interpolator_offset); | |||
// Do not question the sacred code | |||
int shift = ceilf(ratio - offset); | |||
offset = shift + offset - ratio; | |||
n += shift; | |||
int shift = ceilf(ratio - apt->interpolator_offset); | |||
apt->interpolator_offset = shift + apt->interpolator_offset - ratio; | |||
apt->interpolator_n += shift; | |||
} | |||
return count; | |||
} | |||
const float sync_pattern[] = {-1, -1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, | |||
1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 0}; | |||
#define SYNC_SIZE (sizeof(sync_pattern)/sizeof(sync_pattern[0])) | |||
// Get an entire row of pixels, aligned with sync markers | |||
int aptdec_getrow(aptdec_t *apt, float *row, aptdec_callback_t callback, void *context) { | |||
static float pixels[APT_IMG_WIDTH + SYNC_SIZE + 2]; | |||
// Wrap the circular buffer | |||
memcpy(pixels, &pixels[APT_IMG_WIDTH], (SYNC_SIZE + 2) * sizeof(float)); | |||
memcpy(apt->row_buffer, &apt->row_buffer[APT_IMG_WIDTH], (SYNC_SIZE + 2) * sizeof(float)); | |||
// Get a lines worth (APT_IMG_WIDTH) of samples | |||
if (get_pixels(apt, &pixels[SYNC_SIZE + 2], APT_IMG_WIDTH, callback, context) != APT_IMG_WIDTH) { | |||
if (get_pixels(apt, &apt->row_buffer[SYNC_SIZE + 2], APT_IMG_WIDTH, callback, context) != APT_IMG_WIDTH) { | |||
return 0; | |||
} | |||
@@ -213,9 +213,9 @@ int aptdec_getrow(aptdec_t *apt, float *row, aptdec_callback_t callback, void *c | |||
size_t phase = 0; | |||
for (size_t i = 0; i < APT_IMG_WIDTH; i++) { | |||
float _left = convolve(&pixels[i + 0], sync_pattern, SYNC_SIZE); | |||
float _middle = convolve(&pixels[i + 1], sync_pattern, SYNC_SIZE); | |||
float _right = convolve(&pixels[i + 2], sync_pattern, SYNC_SIZE); | |||
float _left = convolve(&apt->row_buffer[i + 0], sync_pattern, SYNC_SIZE); | |||
float _middle = convolve(&apt->row_buffer[i + 1], sync_pattern, SYNC_SIZE); | |||
float _right = convolve(&apt->row_buffer[i + 2], sync_pattern, SYNC_SIZE); | |||
if (_middle > middle) { | |||
left = _left; | |||
middle = _middle; | |||
@@ -226,11 +226,11 @@ int aptdec_getrow(aptdec_t *apt, float *row, aptdec_callback_t callback, void *c | |||
// Frequency | |||
float bias = (left / middle) - (right / middle); | |||
apt->sync_frequency = 1.0f + bias / APT_IMG_WIDTH / 2.0f; | |||
apt->sync_frequency = 1.0f + bias / APT_IMG_WIDTH / 4.0f; | |||
// Phase | |||
memcpy(&row[APT_IMG_WIDTH], &pixels[phase], (APT_IMG_WIDTH - phase) * sizeof(float)); | |||
memcpy(&row[APT_IMG_WIDTH - phase], pixels, phase * sizeof(float)); | |||
memcpy(&row[APT_IMG_WIDTH], &apt->row_buffer[phase], (APT_IMG_WIDTH - phase) * sizeof(float)); | |||
memcpy(&row[APT_IMG_WIDTH - phase], apt->row_buffer, phase * sizeof(float)); | |||
return 1; | |||
} |
@@ -20,6 +20,7 @@ | |||
#include <string.h> | |||
#include <stdio.h> | |||
#include <math.h> | |||
#include <stdlib.h> | |||
#include "algebra.h" | |||
#include "util.h" | |||