Merge branch 'LongHairedHacker/gr-satnogs-noaa-apt-sink'
This commit is contained in:
commit
fd75575828
|
@ -48,7 +48,7 @@ list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules)
|
|||
# Set the version information here
|
||||
set(VERSION_INFO_MAJOR_VERSION 1)
|
||||
set(VERSION_INFO_API_COMPAT 2)
|
||||
set(VERSION_INFO_MINOR_VERSION 1)
|
||||
set(VERSION_INFO_MINOR_VERSION 2)
|
||||
set(VERSION_INFO_MAINT_VERSION git)
|
||||
|
||||
########################################################################
|
||||
|
@ -130,11 +130,12 @@ find_package(Doxygen)
|
|||
find_package(Volk REQUIRED)
|
||||
find_package(OggVorbis REQUIRED)
|
||||
find_package(PNG REQUIRED)
|
||||
find_package(png++ REQUIRED)
|
||||
|
||||
########################################################################
|
||||
# Include or not into the module blocks for debugging
|
||||
########################################################################
|
||||
option(INCLUDE_DEBUG_BLOCKS
|
||||
option(INCLUDE_DEBUG_BLOCKS
|
||||
"Enable/Disable blocks that are used for debugging purposes" ON)
|
||||
|
||||
|
||||
|
|
11
README.md
11
README.md
|
@ -9,9 +9,10 @@ for decoding signals from various scientific and academic sattelites.
|
|||
* CMake ( > 3.1)
|
||||
* G++ (with C++11 support)
|
||||
* VOLK
|
||||
* libogg
|
||||
* libogg
|
||||
* libvorbis
|
||||
* libpng
|
||||
* libpng++
|
||||
* git
|
||||
|
||||
**Optional**
|
||||
|
@ -34,12 +35,12 @@ If this is the first time you are building the gr-satnogs module run
|
|||
By default, the **SatNOGS** module will use the default installation prefix.
|
||||
This highly depends on the Linux distribution. You can use the `CMAKE_INSTALL_PREFIX`
|
||||
variable to alter the default installation path.
|
||||
E.g:
|
||||
E.g:
|
||||
|
||||
`cmake -DCMAKE_INSTALL_PREFIX=/usr ..`
|
||||
|
||||
Also, by default the build system enables a set of blocks used for debugging
|
||||
during the development. The enable/disable switch is controled through the
|
||||
during the development. The enable/disable switch is controled through the
|
||||
`INCLUDE_DEBUG_BLOCKS` boolean variable. If for example, you want to disable the
|
||||
debugging blocks, the **CMake** command would be:
|
||||
|
||||
|
@ -62,6 +63,6 @@ For more indormation about SatNOGS please visit our [site](https://satnogs.org/)
|
|||
|
||||
## License
|
||||
|
||||
© 2016,2017 [Libre Space Foundation](http://librespacefoundation.org).
|
||||
© 2016,2017,2018 [Libre Space Foundation](http://librespacefoundation.org).
|
||||
|
||||
Licensed under the [GPLv3](LICENSE).
|
||||
Licensed under the [GPLv3](LICENSE).
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
# - Try to find png++
|
||||
#
|
||||
# The following variables are optionally searched for defaults
|
||||
# png++_ROOT_DIR: Base directory where all GLOG components are found
|
||||
#
|
||||
# The following are set after configuration is done:
|
||||
# png++_FOUND
|
||||
# png++_INCLUDE_DIRS
|
||||
# png++_LIBRARIES
|
||||
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
set(png++_ROOT_DIR "" CACHE PATH "Folder contains png++")
|
||||
|
||||
find_path(png++_INCLUDE_DIR
|
||||
NAMES
|
||||
png++/color.hpp
|
||||
png++/config.hpp
|
||||
png++/consumer.hpp
|
||||
png++/convert_color_space.hpp
|
||||
png++/end_info.hpp
|
||||
png++/error.hpp
|
||||
png++/ga_pixel.hpp
|
||||
png++/generator.hpp
|
||||
png++/gray_pixel.hpp
|
||||
png++/image.hpp
|
||||
png++/image_info.hpp
|
||||
png++/index_pixel.hpp
|
||||
png++/info.hpp
|
||||
png++/info_base.hpp
|
||||
png++/io_base.hpp
|
||||
png++/packed_pixel.hpp
|
||||
png++/palette.hpp
|
||||
png++/pixel_buffer.hpp
|
||||
png++/pixel_traits.hpp
|
||||
png++/png.hpp
|
||||
png++/reader.hpp
|
||||
png++/require_color_space.hpp
|
||||
png++/rgb_pixel.hpp
|
||||
png++/rgba_pixel.hpp
|
||||
png++/streaming_base.hpp
|
||||
png++/tRNS.hpp
|
||||
png++/types.hpp
|
||||
png++/writer.hpp
|
||||
PATHS
|
||||
${png++_ROOT_DIR}
|
||||
PATH_SUFFIXES
|
||||
src)
|
||||
|
||||
set(png++_INCLUDE_DIRS ${png++_INCLUDE_DIR} ${PNG_INCLUDE_DIRS})
|
||||
set(png++_LIBRARIES ${PNG_LIBRARIES})
|
||||
|
||||
find_package_handle_standard_args(png++ DEFAULT_MSG
|
||||
png++_INCLUDE_DIR)
|
||||
|
||||
if(png++_FOUND)
|
||||
set(png++_INCLUDE_DIRS ${png++_INCLUDE_DIR})
|
||||
set(png++_LIBRARIES ${png++_LIBRARY})
|
||||
endif()
|
|
@ -28,6 +28,7 @@ include_directories(
|
|||
${VORBIS_INCLUDE_DIR}
|
||||
${OGG_INCLUDE_DIR}
|
||||
${PNG_INCLUDE_DIR}
|
||||
${png++_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
link_directories(${Boost_LIBRARY_DIRS})
|
||||
|
@ -36,14 +37,14 @@ list(APPEND satnogs_debug_sources
|
|||
morse_debug_source_impl.cc
|
||||
debug_msg_source_impl.cc
|
||||
debug_msg_source_raw_impl.cc
|
||||
leo_channel_impl.cc
|
||||
leo_channel_impl.cc
|
||||
cw_encoder_impl.cc
|
||||
)
|
||||
list(APPEND satnogs_sources
|
||||
morse_tree.cc
|
||||
morse_decoder_impl.cc
|
||||
multi_format_msg_sink_impl.cc
|
||||
ogg_encoder_impl.cc
|
||||
ogg_encoder_impl.cc
|
||||
cw_to_symbol_impl.cc
|
||||
sine_matched_filter_ff_impl.cc
|
||||
udp_msg_source_impl.cc
|
||||
|
@ -77,16 +78,17 @@ if(NOT satnogs_sources)
|
|||
endif(NOT satnogs_sources)
|
||||
|
||||
add_library(gnuradio-satnogs SHARED ${satnogs_sources})
|
||||
target_link_libraries(gnuradio-satnogs
|
||||
${Boost_LIBRARIES}
|
||||
target_link_libraries(gnuradio-satnogs
|
||||
${Boost_LIBRARIES}
|
||||
${GNURADIO_ALL_LIBRARIES}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${NOVA_LIBRARIES}
|
||||
${VOLK_LIBRARIES}
|
||||
${OGGVORBIS_LIBRARIES}
|
||||
${PNG_LIBRARIES}
|
||||
${png++_LIBRARIES}
|
||||
)
|
||||
|
||||
|
||||
set_target_properties(gnuradio-satnogs PROPERTIES DEFINE_SYMBOL "gnuradio_satnogs_EXPORTS")
|
||||
|
||||
if(APPLE)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
|
||||
*
|
||||
* Copyright (C) 2017, Libre Space Foundation <http://librespacefoundation.org/>
|
||||
* Copyright (C) 2017,2018 Libre Space Foundation <http://librespacefoundation.org/>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -31,6 +31,32 @@ namespace gr
|
|||
{
|
||||
namespace satnogs
|
||||
{
|
||||
// Noaa apt sync pattern A
|
||||
// (see https://sourceforge.isae.fr/attachments/download/1813/apt_synch.gif)
|
||||
const bool noaa_apt_sink_impl::synca_seq[] = {false, false, false, false,
|
||||
true, true, false, false, // Pulse 1
|
||||
true, true, false, false, // Pulse 2
|
||||
true, true, false, false, // Pulse 3
|
||||
true, true, false, false, // Pulse 4
|
||||
true, true, false, false, // Pulse 5
|
||||
true, true, false, false, // Pulse 6
|
||||
true, true, false, false, // Pulse 7
|
||||
false, false, false, false,
|
||||
false, false, false, false};
|
||||
|
||||
// Noaa apt sync pattern B
|
||||
// (see https://sourceforge.isae.fr/attachments/download/1813/apt_synch.gif)
|
||||
const bool noaa_apt_sink_impl::syncb_seq[] = {false, false, false, false,
|
||||
true, true, true, false, false,
|
||||
true, true, true, false, false,
|
||||
true, true, true, false, false,
|
||||
true, true, true, false, false,
|
||||
true, true, true, false, false,
|
||||
true, true, true, false, false,
|
||||
true, true, true, false, false,
|
||||
false};
|
||||
|
||||
|
||||
|
||||
noaa_apt_sink::sptr
|
||||
noaa_apt_sink::make (const char *filename_png, size_t width, size_t height,
|
||||
|
@ -41,6 +67,7 @@ namespace gr
|
|||
flip));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The private constructor
|
||||
*/
|
||||
|
@ -50,338 +77,283 @@ namespace gr
|
|||
gr::sync_block ("noaa_apt_sink",
|
||||
gr::io_signature::make (1, 1, sizeof(float)),
|
||||
gr::io_signature::make (0, 0, 0)),
|
||||
d_sync_word (0x0ccccccc),
|
||||
d_constructed_word (0),
|
||||
d_slicer_threshold (0.5),
|
||||
d_sync_found (false),
|
||||
d_norm_window (2048),
|
||||
d_sample_counter (0),
|
||||
f_average_alpha (0.25),
|
||||
d_row_write_threshold (250),
|
||||
d_filename_png (filename_png),
|
||||
d_width (width),
|
||||
d_height (height),
|
||||
d_split (split),
|
||||
d_history_length (28),
|
||||
d_synchronize_opt (sync),
|
||||
d_row_counter (0),
|
||||
d_flip (flip),
|
||||
d_history_length (40),
|
||||
d_has_sync (false),
|
||||
d_current_x (0),
|
||||
d_current_y (0),
|
||||
d_num_images (0),
|
||||
d_current_buffered_samples (0),
|
||||
d_flip (flip)
|
||||
|
||||
f_max_level(0.0),
|
||||
f_min_level(1.0),
|
||||
f_average(0.0)
|
||||
{
|
||||
set_history (d_history_length);
|
||||
if (sync) {
|
||||
d_max_value = -1;
|
||||
d_min_value = 100;
|
||||
}
|
||||
else {
|
||||
d_max_value = 1;
|
||||
d_min_value = 0;
|
||||
}
|
||||
d_color_type = PNG_COLOR_TYPE_GRAY;
|
||||
d_bit_depth = 8;
|
||||
d_row_buffer = new uint8_t[d_width];
|
||||
if (d_split) {
|
||||
d_png_fn.push_back (std::string (""));
|
||||
d_png_fn.push_back (std::string (""));
|
||||
}
|
||||
else {
|
||||
d_png_fn.push_back (std::string (""));
|
||||
}
|
||||
init_png ();
|
||||
set_history(d_history_length);
|
||||
init_images();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
noaa_apt_sink_impl::init_png ()
|
||||
noaa_apt_sink_impl::init_images () {
|
||||
// Split the filename option into path + filename and extension
|
||||
|
||||
|
||||
// Construct numbered filename for the full image
|
||||
d_full_filename = d_filename_png + std::to_string(d_num_images);
|
||||
// Create a new empty png
|
||||
d_full_image = png::image<png::gray_pixel>(d_width, d_height);
|
||||
|
||||
|
||||
if(d_split) {
|
||||
// In case split images are requested construct filenames for those as well
|
||||
d_left_filename = d_filename_png + std::to_string(d_num_images)
|
||||
+ "_left";
|
||||
d_right_filename = d_filename_png + std::to_string(d_num_images)
|
||||
+ "_right";
|
||||
|
||||
// Create new empty pngs for the split images
|
||||
d_left_image = png::image<png::gray_pixel>(d_width/2, d_height);
|
||||
d_right_image = png::image<png::gray_pixel>(d_width/2, d_height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
noaa_apt_sink_impl::write_image (png::image<png::gray_pixel> image,
|
||||
std::string filename)
|
||||
{
|
||||
std::string fn (d_filename_png);
|
||||
if (d_split) {
|
||||
d_images_per_frame = 2;
|
||||
d_png_fd = new FILE*[2];
|
||||
d_png_ptr = new png_structp[2];
|
||||
d_info_ptr = new png_infop[2];
|
||||
std::string fn_left = fn;
|
||||
std::string fn_right = fn;
|
||||
if (d_num_images == 0) {
|
||||
fn_left.append ("_left");
|
||||
fn_right.append ("_right");
|
||||
// In case the flip option is set
|
||||
if(d_flip) {
|
||||
size_t width = image.get_width();
|
||||
size_t height = image.get_height();
|
||||
|
||||
// An image of same size is created ...
|
||||
png::image<png::gray_pixel> flipped(width, height);
|
||||
|
||||
// ... and all the lines are copied over reverse order
|
||||
for(size_t y = 0; y < height; y++) {
|
||||
for(size_t x = 0; x < width; x++) {
|
||||
png::gray_pixel pixel = image.get_pixel(x, height - y - 1);
|
||||
flipped.set_pixel(x, y, pixel);
|
||||
}
|
||||
}
|
||||
// Write out the flipped image
|
||||
flipped.write(filename);
|
||||
}
|
||||
// In case the flip option is not set
|
||||
else {
|
||||
// Write out the original
|
||||
image.write(filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
noaa_apt_sink_impl::write_images () {
|
||||
// Write out the full image
|
||||
write_image(d_full_image, d_full_filename);
|
||||
|
||||
if(d_split) {
|
||||
// Write out the split images if the split option is enabled
|
||||
write_image(d_left_image, d_left_filename);
|
||||
write_image(d_right_image, d_right_filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
noaa_apt_sink_impl::~noaa_apt_sink_impl () {
|
||||
// Nothing happens here
|
||||
}
|
||||
|
||||
bool
|
||||
noaa_apt_sink_impl::stop () {
|
||||
// As a teardown action, the remaining pngs
|
||||
//should be cropped to the correct size and written to disk
|
||||
|
||||
// Grab the buffers from the fullsize pngs
|
||||
png::image<png::gray_pixel>::pixbuf buf_full_image = d_full_image.get_pixbuf();
|
||||
png::image<png::gray_pixel>::pixbuf buf_left_image = d_left_image.get_pixbuf();
|
||||
png::image<png::gray_pixel>::pixbuf buf_right_image = d_right_image.get_pixbuf();
|
||||
|
||||
// Create new smaller pngs using the old buffers
|
||||
d_full_image = png::image<png::gray_pixel>(d_width, d_current_y + 1);
|
||||
d_full_image.set_pixbuf(buf_full_image);
|
||||
|
||||
d_left_image = png::image<png::gray_pixel>(d_width/2, d_current_y + 1);
|
||||
d_left_image .set_pixbuf(buf_left_image);
|
||||
|
||||
d_right_image = png::image<png::gray_pixel>(d_width/2, d_current_y + 1);
|
||||
d_right_image.set_pixbuf(buf_right_image);
|
||||
|
||||
// Write the smaller images to disk
|
||||
write_images();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void noaa_apt_sink_impl::set_pixel (size_t x, size_t y, float sample) {
|
||||
// Adjust dynamic range, using minimum and maximum values
|
||||
sample = (sample - f_min_level) / (f_max_level - f_min_level) * 255;
|
||||
// Set the pixel in the full image
|
||||
d_full_image.set_pixel(x, y, sample);
|
||||
|
||||
// Id the split otions is set
|
||||
if(d_split) {
|
||||
// Set the pixel in the right image,
|
||||
// depending on its coordinate
|
||||
if(x < d_width / 2) {
|
||||
d_left_image.set_pixel(x, y, sample);
|
||||
}
|
||||
else {
|
||||
d_right_image.set_pixel(x - d_width / 2, y, sample);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
noaa_apt_sink_impl::skip_to (size_t new_x, size_t pos, const float *samples) {
|
||||
// Check if the skip is forward or backward
|
||||
if(new_x > d_current_x) {
|
||||
// In case it is forward there will be a new_x - d_current_x sized hole
|
||||
// in the image. Holes up 39 pixels can be filled from the modules history
|
||||
size_t dist = std::min(size_t(39), new_x - d_current_x);
|
||||
// Fill the hole using the previous samples of pos
|
||||
for(size_t i = 0; i < dist; i++) {
|
||||
set_pixel(new_x - dist + i, d_current_y, samples[pos - dist + i]);
|
||||
}
|
||||
}
|
||||
// Jump to new location
|
||||
d_current_x = new_x;
|
||||
}
|
||||
|
||||
|
||||
noaa_apt_sync_marker
|
||||
noaa_apt_sink_impl::is_marker(size_t pos, const float *samples) {
|
||||
// Initialize counters for 'hacky' correlation
|
||||
size_t count_a = 0;
|
||||
size_t count_b = 0;
|
||||
|
||||
for(size_t i = 0; i < 40; i++) {
|
||||
// history of previous 39 samples + current one
|
||||
// -> start 39 samples in the past
|
||||
float sample = samples[pos - 39 + i];
|
||||
// Remove DC-offset (aka. the average value of the sync pattern)
|
||||
sample = sample - f_average;
|
||||
|
||||
// Very basic 1/0 correlation between pattern constan and history
|
||||
if((sample > 0 && synca_seq[i]) || (sample < 0 && !syncb_seq[i])) {
|
||||
count_a += 1;
|
||||
}
|
||||
if((sample > 0 && syncb_seq[i]) || (sample < 0 && !syncb_seq[i])) {
|
||||
count_b += 1;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Prefer sync pattern a as it is detected more reliable
|
||||
if(count_a > 35) {
|
||||
return noaa_apt_sync_marker::SYNC_A;
|
||||
}
|
||||
else if(count_b > 35) {
|
||||
return noaa_apt_sync_marker::SYNC_B;
|
||||
}
|
||||
else {
|
||||
fn_left.append (std::to_string (d_num_images).append ("_left"));
|
||||
fn_right.append (std::to_string (d_num_images).append ("_right"));
|
||||
return noaa_apt_sync_marker::NONE;
|
||||
}
|
||||
d_png_fn[0] = fn_left;
|
||||
d_png_fn[1] = fn_right;
|
||||
d_png_fd[0] = fopen (fn_right.c_str (), "wb");
|
||||
d_png_fd[1] = fopen (fn_left.c_str (), "wb");
|
||||
for (size_t i = 0; i < d_images_per_frame; i++) {
|
||||
d_png_ptr[i] = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
d_info_ptr[i] = png_create_info_struct (d_png_ptr[i]);
|
||||
png_init_io (d_png_ptr[i], d_png_fd[i]);
|
||||
png_set_IHDR (d_png_ptr[i], d_info_ptr[i], d_width / 2, d_height,
|
||||
d_bit_depth, d_color_type,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_BASE,
|
||||
PNG_FILTER_TYPE_BASE);
|
||||
|
||||
png_write_info (d_png_ptr[i], d_info_ptr[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
d_images_per_frame = 1;
|
||||
d_png_fd = new FILE*[1];
|
||||
d_png_ptr = new png_structp[2];
|
||||
d_info_ptr = new png_infop[2];
|
||||
if (d_num_images == 0) {
|
||||
d_png_fd[0] = fopen (fn.c_str (), "wb");
|
||||
d_png_fn[0] = fn;
|
||||
}
|
||||
else {
|
||||
fn.append (std::to_string (d_num_images));
|
||||
d_png_fd[0] = fopen (fn.c_str (), "wb");
|
||||
d_png_fn[0] = fn;
|
||||
}
|
||||
|
||||
d_png_ptr[0] = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
d_info_ptr[0] = png_create_info_struct (d_png_ptr[0]);
|
||||
png_init_io (d_png_ptr[0], d_png_fd[0]);
|
||||
png_set_IHDR (d_png_ptr[0], d_info_ptr[0], d_width, d_height,
|
||||
d_bit_depth, d_color_type,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_BASE,
|
||||
PNG_FILTER_TYPE_BASE);
|
||||
png_write_info (d_png_ptr[0], d_info_ptr[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
noaa_apt_sink_impl::write_png_row ()
|
||||
{
|
||||
if (d_row_counter == d_height) {
|
||||
for (size_t i = 0; i < d_images_per_frame; i++) {
|
||||
png_write_end (d_png_ptr[i], NULL);
|
||||
fclose (d_png_fd[i]);
|
||||
}
|
||||
if (d_flip) {
|
||||
flip_image ();
|
||||
}
|
||||
d_row_counter = 0;
|
||||
d_num_images++;
|
||||
init_png ();
|
||||
}
|
||||
if (d_split) {
|
||||
for (size_t i = 0; i < d_images_per_frame; i++) {
|
||||
png_write_row (d_png_ptr[i], d_row_buffer + i * d_width / 2);
|
||||
}
|
||||
}
|
||||
else {
|
||||
png_write_row (d_png_ptr[0], d_row_buffer);
|
||||
}
|
||||
d_row_counter++;
|
||||
}
|
||||
noaa_apt_sink_impl::~noaa_apt_sink_impl ()
|
||||
{
|
||||
if (d_current_buffered_samples < d_width) {
|
||||
memset (d_row_buffer + d_current_buffered_samples, 0,
|
||||
(d_width - d_current_buffered_samples) * sizeof(uint8_t));
|
||||
}
|
||||
write_png_row ();
|
||||
if (d_row_counter < d_height) {
|
||||
size_t row_count = d_row_counter;
|
||||
for (size_t i = 0; i < d_height - row_count; i++) {
|
||||
memset (d_row_buffer, 0, d_width * sizeof(uint8_t));
|
||||
write_png_row ();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < d_images_per_frame; i++) {
|
||||
png_write_end (d_png_ptr[i], NULL);
|
||||
fclose (d_png_fd[i]);
|
||||
}
|
||||
if (d_flip) {
|
||||
flip_image ();
|
||||
}
|
||||
delete[] d_row_buffer;
|
||||
delete[] d_png_fd;
|
||||
delete[] d_png_ptr;
|
||||
delete[] d_info_ptr;
|
||||
}
|
||||
|
||||
void
|
||||
noaa_apt_sink_impl::flip_image ()
|
||||
{
|
||||
int height;
|
||||
png_byte color_type;
|
||||
png_byte bit_depth;
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
int number_of_passes;
|
||||
png_bytep* row_pointers;
|
||||
size_t width;
|
||||
if (d_split) {
|
||||
width = d_width / 2;
|
||||
}
|
||||
else {
|
||||
width = d_width;
|
||||
}
|
||||
for (size_t i = 0; i < d_images_per_frame; i++) {
|
||||
char header[8]; // 8 is the maximum size that can be checked
|
||||
d_png_fd[i] = fopen (d_png_fn[i].c_str (), "rb");
|
||||
fread (header, 1, 8, d_png_fd[i]);
|
||||
|
||||
/* initialize stuff */
|
||||
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL,
|
||||
NULL);
|
||||
|
||||
info_ptr = png_create_info_struct (png_ptr);
|
||||
|
||||
png_init_io (png_ptr, d_png_fd[i]);
|
||||
png_set_sig_bytes (png_ptr, 8);
|
||||
png_read_info (png_ptr, info_ptr);
|
||||
|
||||
png_read_update_info (png_ptr, info_ptr);
|
||||
|
||||
row_pointers = new png_bytep[d_height];
|
||||
for (size_t y = 0; y < d_height; y++) {
|
||||
row_pointers[y] = new png_byte[png_get_rowbytes (png_ptr, info_ptr)];
|
||||
}
|
||||
png_read_image (png_ptr, row_pointers);
|
||||
|
||||
fclose (d_png_fd[i]);
|
||||
d_png_fd[i] = fopen (d_png_fn[i].c_str (), "wb");
|
||||
d_png_ptr[i] = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
d_info_ptr[i] = png_create_info_struct (d_png_ptr[i]);
|
||||
png_init_io (d_png_ptr[i], d_png_fd[i]);
|
||||
png_set_IHDR (d_png_ptr[i], d_info_ptr[i], width, d_height, d_bit_depth,
|
||||
d_color_type,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_BASE,
|
||||
PNG_FILTER_TYPE_BASE);
|
||||
|
||||
png_write_info (d_png_ptr[i], d_info_ptr[i]);
|
||||
for (int j = d_height - 1; j >= 0; j--) {
|
||||
uint8_t *istart = row_pointers[j];
|
||||
uint8_t *iend = istart + width;
|
||||
std::reverse (row_pointers[j], iend);
|
||||
png_write_row (d_png_ptr[i], row_pointers[j]);
|
||||
}
|
||||
png_write_end (d_png_ptr[i], NULL);
|
||||
fclose (d_png_fd[i]);
|
||||
|
||||
for (size_t y = 0; y < d_height; y++) {
|
||||
delete[] row_pointers[y];
|
||||
}
|
||||
delete[] row_pointers;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
noaa_apt_sink_impl::work (int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items)
|
||||
{
|
||||
const float *in = (const float *) input_items[0];
|
||||
uint8_t b = 0;
|
||||
float sample;
|
||||
long int r;
|
||||
const float *in = (const float *) input_items[0];
|
||||
|
||||
for (size_t i = d_history_length - 1;
|
||||
i < noutput_items + d_history_length - 1; i++) {
|
||||
if (!d_synchronize_opt) {
|
||||
sample = 255 * in[i];
|
||||
r = (long int) rint (sample);
|
||||
if (r < 0)
|
||||
r = 0;
|
||||
else if (r > 255)
|
||||
r = 255;
|
||||
d_row_buffer[d_current_buffered_samples] = (uint8_t) r;
|
||||
d_current_buffered_samples++;
|
||||
if (d_current_buffered_samples == d_width) {
|
||||
write_png_row ();
|
||||
d_current_buffered_samples = 0;
|
||||
}
|
||||
// Structure of in[]:
|
||||
// - d_history_length many historical samples
|
||||
// - noutput_items many samples to process
|
||||
for (size_t i = d_history_length - 1;
|
||||
i < noutput_items + d_history_length - 1; i++) {
|
||||
|
||||
// Get the current sample
|
||||
float sample = in[i];
|
||||
|
||||
// Update min and max level to adjust dynamic range in set pixel
|
||||
f_max_level = std::fmax(f_max_level, sample);
|
||||
f_min_level = std::fmin(f_min_level, sample);
|
||||
|
||||
// Update exponential smoothing average used in sync pattern detection
|
||||
f_average = f_average_alpha * sample + (1.0 - f_average_alpha) * f_average;
|
||||
|
||||
// If line sync is enabled
|
||||
if(d_synchronize_opt) {
|
||||
// Check if the history for the current sample is a sync pattern
|
||||
noaa_apt_sync_marker marker = is_marker(i, in);
|
||||
|
||||
// For pattern a
|
||||
if(marker == noaa_apt_sync_marker::SYNC_A) {
|
||||
// Skip to right location, pattern starts 40 samples in the past
|
||||
skip_to(39, i, in);
|
||||
// If this is the first sync, reset min and max
|
||||
if(!d_has_sync) {
|
||||
f_max_level = 0.0;
|
||||
f_min_level = 1.0;
|
||||
d_has_sync = true;
|
||||
}
|
||||
}
|
||||
// For pattern b
|
||||
else if(marker == noaa_apt_sync_marker::SYNC_B) {
|
||||
// Skip to right location, pattern starts 40 samples in the past
|
||||
skip_to(d_width / 2 + 39, i, in);
|
||||
// If this is the first sync, reset min and max
|
||||
if(!d_has_sync) {
|
||||
f_max_level = 0.0;
|
||||
f_min_level = 1.0;
|
||||
d_has_sync = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the the pixel at the current position
|
||||
set_pixel(d_current_x, d_current_y, sample);
|
||||
|
||||
// Increment x position
|
||||
d_current_x += 1;
|
||||
// If we are beyond the end of line
|
||||
if(d_current_x >= d_width) {
|
||||
// Increment y position
|
||||
d_current_y += 1;
|
||||
// Reset x position to line start
|
||||
d_current_x = 0;
|
||||
|
||||
// If there are enough lines decoded write the image to disk
|
||||
if(d_current_y % d_row_write_threshold == 0) {
|
||||
write_images();
|
||||
}
|
||||
|
||||
// Split the image if there are enough lines decoded
|
||||
if(d_current_y >= d_height) {
|
||||
d_current_y = 0;
|
||||
d_num_images += 1;
|
||||
write_images();
|
||||
init_images();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (d_sync_found) {
|
||||
if (d_sample_counter < d_norm_window) {
|
||||
if (in[i] < d_min_value) {
|
||||
d_min_value = in[i];
|
||||
}
|
||||
if (in[i] > d_max_value) {
|
||||
d_max_value = in[i];
|
||||
}
|
||||
d_sample_counter++;
|
||||
}
|
||||
sample = ((in[i] - d_min_value) / (d_max_value - d_min_value));
|
||||
sample = 255 * sample;
|
||||
r = (long int) rint (sample);
|
||||
if (r < 0) {
|
||||
r = 0;
|
||||
}
|
||||
else if (r > 255) {
|
||||
r = 255;
|
||||
}
|
||||
d_row_buffer[d_current_buffered_samples] = (uint8_t) r;
|
||||
d_current_buffered_samples++;
|
||||
if (d_current_buffered_samples == d_width) {
|
||||
write_png_row ();
|
||||
d_current_buffered_samples = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (d_sample_counter < d_norm_window) {
|
||||
if (in[i] < d_min_value) {
|
||||
d_min_value = in[i];
|
||||
}
|
||||
if (in[i] > d_max_value) {
|
||||
d_max_value = in[i];
|
||||
}
|
||||
d_sample_counter++;
|
||||
d_slicer_threshold = (d_max_value + d_min_value)/2.0;
|
||||
continue;
|
||||
}
|
||||
b = in[i] > d_slicer_threshold ? 1 : 0;
|
||||
d_constructed_word = (d_constructed_word << 1) | b;
|
||||
if (d_constructed_word == d_sync_word) {
|
||||
d_sync_found = true;
|
||||
d_sample_counter =0;
|
||||
for (size_t j = i - (d_history_length - 1); j <= i; j++) {
|
||||
if (in[j] < d_min_value) {
|
||||
d_min_value = in[i];
|
||||
}
|
||||
if (in[j] > d_max_value) {
|
||||
d_max_value = in[j];
|
||||
}
|
||||
}
|
||||
for (size_t j = i - (d_history_length - 1); j <= i; j++) {
|
||||
sample = ((in[i] - d_min_value) / (d_max_value - d_min_value));
|
||||
sample = 255 * sample;
|
||||
r = (long int) rint (sample);
|
||||
if (r < 0) {
|
||||
r = 0;
|
||||
}
|
||||
else if (r > 255) {
|
||||
r = 255;
|
||||
}
|
||||
d_row_buffer[d_current_buffered_samples] = (uint8_t) r;
|
||||
d_current_buffered_samples++;
|
||||
if (d_current_buffered_samples == d_width) {
|
||||
write_png_row ();
|
||||
d_current_buffered_samples = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noutput_items;
|
||||
|
||||
// Tell gnu radio how many samples were consumed
|
||||
return noutput_items;
|
||||
}
|
||||
|
||||
} /* namespace satnogs */
|
||||
} /* namespace gr */
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* gr-satnogs: SatNOGS GNU Radio Out-Of-Tree Module
|
||||
*
|
||||
* Copyright (C) 2017, Libre Space Foundation <http://librespacefoundation.org/>
|
||||
* Copyright (C) 2017,2018 Libre Space Foundation <http://librespacefoundation.org/>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -23,63 +23,103 @@
|
|||
|
||||
#include <satnogs/noaa_apt_sink.h>
|
||||
#define PNG_DEBUG 3
|
||||
#include <png.h>
|
||||
#include <png++/png.hpp>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
|
||||
namespace gr
|
||||
{
|
||||
namespace satnogs
|
||||
{
|
||||
enum class noaa_apt_sync_marker {SYNC_A, SYNC_B, NONE};
|
||||
|
||||
class noaa_apt_sink_impl : public noaa_apt_sink
|
||||
{
|
||||
private:
|
||||
uint32_t d_sync_word;
|
||||
uint32_t d_constructed_word;
|
||||
float d_slicer_threshold;
|
||||
bool d_sync_found;
|
||||
float d_max_value;
|
||||
float d_min_value;
|
||||
size_t d_norm_window;
|
||||
size_t d_sample_counter;
|
||||
const char* d_filename_png;
|
||||
// Factor exponential smoothing average,
|
||||
// which is used for sync pattern detection
|
||||
const float f_average_alpha;
|
||||
|
||||
// The images are written to disk every d_row_write_threshold lines
|
||||
// so in case something goes horribly wrong, partial images will be available
|
||||
const size_t d_row_write_threshold;
|
||||
|
||||
|
||||
static const bool synca_seq[];
|
||||
static const bool syncb_seq[];
|
||||
|
||||
std::string d_filename_png;
|
||||
size_t d_width;
|
||||
size_t d_height;
|
||||
bool d_split;
|
||||
size_t d_history_length;
|
||||
bool d_synchronize_opt;
|
||||
png_structp* d_png_ptr;
|
||||
png_infop* d_info_ptr;
|
||||
uint8_t* d_row_buffer;
|
||||
png_byte d_color_type;
|
||||
png_byte d_bit_depth;
|
||||
FILE** d_png_fd;
|
||||
size_t d_images_per_frame;
|
||||
size_t d_row_counter;
|
||||
size_t d_num_images;
|
||||
size_t d_current_buffered_samples;
|
||||
std::vector<std::string> d_png_fn;
|
||||
bool d_flip;
|
||||
size_t d_history_length;
|
||||
bool d_has_sync;
|
||||
|
||||
png::image<png::gray_pixel> d_full_image;
|
||||
png::image<png::gray_pixel> d_left_image;
|
||||
png::image<png::gray_pixel> d_right_image;
|
||||
std::string d_full_filename;
|
||||
std::string d_left_filename;
|
||||
std::string d_right_filename;
|
||||
|
||||
size_t d_current_x;
|
||||
size_t d_current_y;
|
||||
size_t d_num_images;
|
||||
|
||||
float f_max_level;
|
||||
float f_min_level;
|
||||
float f_average;
|
||||
|
||||
public:
|
||||
noaa_apt_sink_impl (const char *filename_png, size_t width, size_t height,
|
||||
bool split, bool sync, bool flip);
|
||||
~noaa_apt_sink_impl ();
|
||||
void
|
||||
write_png_row ();
|
||||
void
|
||||
init_png ();
|
||||
void
|
||||
flip_image ();
|
||||
|
||||
// Where all the action really happens
|
||||
int
|
||||
work (int noutput_items, gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items);
|
||||
|
||||
// For teardown actions, like writing the remaining images to disk
|
||||
bool
|
||||
stop ();
|
||||
|
||||
private:
|
||||
// Generates new empty images and the filenames for them
|
||||
void
|
||||
init_images ();
|
||||
|
||||
/*
|
||||
* Checks if the history portion of the input contains a sync marker.
|
||||
* Matches the 40 samples before pos against the patterns.
|
||||
*/
|
||||
noaa_apt_sync_marker
|
||||
is_marker (size_t pos, const float *samples);
|
||||
|
||||
// Sets the pixel indicated by coordinates in the images (both full and split)
|
||||
void
|
||||
set_pixel (size_t x, size_t y, float sample);
|
||||
|
||||
/*
|
||||
* Updates d_current_x to new_x,
|
||||
* while using historical samples to fill any resulting gaps in the images.
|
||||
*/
|
||||
void
|
||||
skip_to (size_t new_x, size_t pos, const float *samples);
|
||||
|
||||
// Writes all images to disk
|
||||
void
|
||||
write_images ();
|
||||
|
||||
// Writes a single image to disk, also takes care of flipping
|
||||
void
|
||||
write_image (png::image<png::gray_pixel> image, std::string filename);
|
||||
};
|
||||
|
||||
} // namespace satnogs
|
||||
} // namespace gr
|
||||
|
||||
#endif /* INCLUDED_SATNOGS_NOAA_APT_SINK_IMPL_H */
|
||||
|
||||
|
|
Loading…
Reference in New Issue