Last active
August 14, 2019 23:31
-
-
Save hhrhhr/0abb39b2fba297285bb16f291398d077 to your computer and use it in GitHub Desktop.
save 8-bit grayscale channels
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdarg.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <time.h> | |
#include <unistd.h> | |
#include "channel.h" | |
#include "correlator.h" | |
#include "diff.h" | |
#include "file.h" | |
#include "options.h" | |
#include "packetizer.h" | |
#include "png_out.h" | |
#include "reedsolomon.h" | |
#include "utils.h" | |
#include "viterbi.h" | |
static void align_channels(Channel *ch[3], int seq); | |
static void align_uninit(Channel *ch[3], int seq); | |
int | |
main(int argc, char *argv[]) | |
{ | |
int i, c; | |
int write_statfile; | |
unsigned int first_tstamp, last_tstamp; | |
int total_count, valid_count; | |
int mcu_nr, seq_delta, last_seq; | |
uint8_t encoded_syncword[2*sizeof(SYNCWORD)]; | |
char *stat_fname; | |
FILE *out_fd[3] = {NULL, NULL, NULL}, *stat_fd = NULL; | |
SoftSource *src, *diff, *correlator; | |
HardSource *viterbi; | |
Packetizer *pp; | |
Channel *ch[3], *cur_ch; | |
Segment seg; | |
Mcu *mcu; | |
/* Command-line changeable variables {{{*/ | |
int apid_list[3]; | |
int diffcoding; | |
int split_channels; | |
char *out_fname[3] = {NULL, NULL, NULL}, *in_fname, *tmp_out_fname; | |
int free_fname_on_exit; | |
int quiet; | |
/*}}}*/ | |
/* Initialize command-line overridable parameters {{{*/ | |
quiet = 0; | |
out_fname[0] = NULL; | |
free_fname_on_exit = 0; | |
write_statfile = 0; | |
diffcoding = 0; | |
apid_list[0] = 68; | |
apid_list[1] = 65; | |
apid_list[2] = 64; | |
split_channels = 0; | |
/*}}}*/ | |
/* Parse command line args {{{ */ | |
if (argc < 2) { | |
usage(argv[0]); | |
} | |
optind = 0; | |
while ((c = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1) { | |
switch(c) { | |
case 'a': | |
parse_apids(apid_list, optarg); | |
break; | |
case 'd': | |
diffcoding = 1; | |
break; | |
case 'h': | |
usage(argv[0]); | |
break; | |
case 'o': | |
tmp_out_fname = optarg; | |
break; | |
case 'q': | |
quiet = 1; | |
break; | |
case 's': | |
write_statfile = 1; | |
break; | |
case 'S': | |
split_channels = 1; | |
break; | |
case 'v': | |
version(); | |
exit(0); | |
} | |
} | |
/* Check that an input filename was given */ | |
if (argc - optind < 1) { | |
usage(argv[0]); | |
} | |
/*}}}*/ | |
in_fname = argv[optind]; | |
if (!tmp_out_fname) { | |
if (split_channels) { | |
out_fname[0] = gen_fname(apid_list[0]); | |
out_fname[1] = gen_fname(apid_list[1]); | |
out_fname[2] = gen_fname(apid_list[2]); | |
} else { | |
out_fname[0] = gen_fname(-1); | |
} | |
free_fname_on_exit = 1; | |
} else if (split_channels) { | |
for (i=0; i<3; i++) { | |
out_fname[i] = safealloc(strlen(tmp_out_fname) + sizeof("-AA.png")); | |
sprintf(out_fname[i], "%s-%02d.png", tmp_out_fname, apid_list[i]); | |
} | |
free_fname_on_exit = 1; | |
} else { | |
out_fname[0] = tmp_out_fname; | |
} | |
splash(); | |
src = src_soft_open(in_fname, 8); | |
viterbi_encode(encoded_syncword, SYNCWORD, sizeof(SYNCWORD)); | |
if (diffcoding) { | |
diff = diff_src(src); | |
correlator = correlator_init_soft(diff, encoded_syncword); | |
} else { | |
diff = NULL; | |
correlator = correlator_init_soft(src, encoded_syncword); | |
} | |
viterbi = viterbi_init(correlator); | |
pp = pkt_init(viterbi); | |
ch[0] = channel_init(apid_list[0]); | |
ch[1] = channel_init(apid_list[1]); | |
ch[2] = channel_init(apid_list[2]); | |
/* Open/create the output PNG/PNGs */ | |
if (!(out_fd[0] = fopen(out_fname[0], "wb"))) { | |
fatal("Could not create/open output file"); | |
} | |
if (split_channels) { | |
if (!(out_fd[1] = fopen(out_fname[1], "wb"))) { | |
fatal("Could not create/open output file"); | |
} | |
if (!(out_fd[2] = fopen(out_fname[2], "wb"))) { | |
fatal("Could not create/open output file"); | |
} | |
} | |
if (write_statfile) { | |
stat_fname = safealloc(strlen(out_fname[0]) + 5 + 1); | |
sprintf(stat_fname, "%s.stat", out_fname[0]); | |
if (!(stat_fd = fopen(stat_fname, "wb"))) { | |
fatal("Could not create/open stat file"); | |
} | |
} | |
last_seq = -1; | |
first_tstamp = -1; | |
last_tstamp = -1; | |
total_count = 0; | |
valid_count = 0; | |
/* Read all the packets */ | |
printf("Decoding started\n"); | |
while (pkt_read(pp, &seg)) { | |
total_count++; | |
if (!quiet) { | |
printf("\033[2K\r"); | |
printf("0x%08X rs=%2d ", pkt_get_marker(pp), pkt_get_rs(pp)); | |
} | |
/* Skip invalid packets */ | |
if (seg.len <= 0) { | |
fflush(stdout); | |
continue; | |
} | |
valid_count++; | |
if (!quiet) { | |
printf("seq=%5d, APID %d %s\r", seg.seq, seg.apid, timeofday(seg.timestamp)); | |
fflush(stdout); | |
} | |
/* Meteor-M2 has some overflow issues, and can send bad frames, which | |
* screw with the sequence numbering (they all have seq=0).Skip them. */ | |
if (seg.apid == 0) { | |
continue; | |
} | |
if (last_seq < 0) { | |
first_tstamp = seg.timestamp; | |
} | |
mcu = (Mcu*)seg.data; | |
mcu_nr = mcu_seq(mcu); | |
seq_delta = (seg.seq - last_seq + MPDU_MAX_SEQ) % MPDU_MAX_SEQ; | |
/* Compensate for lost MCUs */ | |
if (last_seq >= 0 && seq_delta > 1) { | |
align_channels(ch, seg.seq); | |
} | |
/* Keep the uninitialized channels aligned */ | |
if (last_tstamp < seg.timestamp) { | |
align_uninit(ch, seg.seq); | |
} | |
/* Append the received MCU */ | |
for (i=0; i<3; i++) { | |
if (seg.apid == apid_list[i]) { | |
cur_ch = ch[i]; | |
/* Align the beginning of the row */ | |
while (cur_ch->mcu_offset < mcu_nr) { | |
channel_decode(cur_ch, NULL); | |
} | |
channel_decode(cur_ch, &seg); | |
} | |
} | |
last_tstamp = seg.timestamp; | |
last_seq = seg.seq; | |
} | |
if (!quiet) { | |
printf("\n"); | |
} | |
printf("Decoding complete\n"); | |
printf("Successfully decoded packets: %d/%d (%4.1f%%)\n", valid_count, total_count, | |
100.0 * valid_count/total_count); | |
pkt_deinit(pp); | |
viterbi->close(viterbi); | |
correlator->close(correlator); | |
if (diffcoding) { | |
diff->close(diff); | |
} | |
src->close(src); | |
/* Write the 3 decoded channels to a PNG if there's any data in them */ | |
if (valid_count > 0) { | |
if (split_channels) { | |
png_compose(out_fd[0], ch[0], NULL, NULL); | |
png_compose(out_fd[1], ch[1], NULL, NULL); | |
png_compose(out_fd[2], ch[2], NULL, NULL); | |
} else { | |
png_compose(out_fd[0], ch[0], ch[1], ch[2]); | |
} | |
/* Write the .stat file used by software like MeteorGIS */ | |
if (stat_fd) { | |
fprintf(stat_fd, "%s\r\n", timeofday(first_tstamp)); | |
fprintf(stat_fd, "%s\r\n", timeofday(last_tstamp - first_tstamp)); | |
fprintf(stat_fd, "0,1538925\r\n"); | |
} | |
} | |
/* Deinitialize/free all allocated resources */ | |
for (i=0; i<3; i++) { | |
channel_deinit(ch[i]); | |
if (out_fd[i]) { | |
fclose(out_fd[i]); | |
} | |
if (free_fname_on_exit && out_fname[i]) { | |
free(out_fname[i]); | |
} | |
} | |
if (stat_fd) { | |
fclose(stat_fd); | |
} | |
return 0; | |
} | |
/* Static functions {{{ */ | |
/* Align channels to a specific segment number */ | |
void | |
align_channels(Channel *ch[3], int seq) | |
{ | |
Channel *cur_ch; | |
int chnum; | |
int helper_seq; | |
for (chnum=0; chnum<3; chnum++) { | |
cur_ch = ch[chnum]; | |
if (cur_ch->last_seq < 0) { | |
continue; | |
} | |
/* Compute the target sequence number to reach */ | |
helper_seq = (cur_ch->last_seq > seq) ? seq + MPDU_MAX_SEQ : seq; | |
/* Fill the end of the last line */ | |
while (cur_ch->mcu_offset > 0) { | |
if (cur_ch->last_seq == helper_seq - 1) { | |
break; | |
} | |
channel_decode(cur_ch, NULL); | |
cur_ch->last_seq++; | |
} | |
/* Fill the middle rows if necessary */ | |
while (cur_ch->last_seq + MPDU_PER_PERIOD < helper_seq) { | |
channel_newline(cur_ch); | |
cur_ch->last_seq += MPDU_PER_PERIOD; | |
} | |
cur_ch->last_seq %= MPDU_MAX_SEQ; | |
} | |
} | |
/* Maintain the beginning of each channel aligned */ | |
void | |
align_uninit(Channel *ch[3], int seq) | |
{ | |
int i, j, lines_delta; | |
Channel *cur_ch; | |
for (i=0; i<3; i++) { | |
cur_ch = ch[i]; | |
/* Channel is uninitialized: check whether there is another | |
* channel that is initialized */ | |
if (cur_ch->last_seq < 0) { | |
for (j=0; j<3; j++) { | |
if (ch[j]->last_seq > 0) { | |
/* Compute the number of lines to fill */ | |
lines_delta = (seq - ch[j]->last_seq + MPDU_MAX_SEQ) % MPDU_MAX_SEQ; | |
lines_delta /= MPDU_PER_PERIOD; | |
for (; lines_delta >= 0; lines_delta--) { | |
channel_newline(cur_ch); | |
} | |
break; | |
} | |
} | |
} | |
} | |
} | |
/*}}}*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <png.h> | |
#include <stdio.h> | |
#include "channel.h" | |
#include "packet.h" | |
#include "png_out.h" | |
#include "utils.h" | |
#define WIDTH (MCU_PER_PP * 8) | |
void | |
png_compose(FILE *fd, Channel *red, Channel *green, Channel *blue) | |
{ | |
int row, col, block_num, block_offset; | |
size_t pixel_idx; | |
png_byte red_px, green_px, blue_px; | |
int height; | |
int color_depth, channels; | |
png_structp png_ptr; | |
png_infop info_ptr; | |
png_bytep row_ptr; | |
if (!fd) { | |
fatal("Output fd is null"); | |
} | |
channels = (!green && !blue) ? 1 : 3; | |
/* Compute the image height: if it is zero, just return */ | |
if (1 == channels) { | |
height = red->len / WIDTH; | |
} else { | |
height = MAX(red->len, MAX(green->len, blue->len)) / WIDTH; | |
} | |
if (height == 0) { | |
return; | |
} | |
/* Initialize all the png related stuff */ | |
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
if (!png_ptr) { | |
fatal("Could not allocate png write struct"); | |
} | |
info_ptr = png_create_info_struct(png_ptr); | |
if (!info_ptr) { | |
fatal("Could not allocate png info struct"); | |
} | |
if (setjmp(png_jmpbuf(png_ptr))) { | |
fatal("Error during the creation of the png"); | |
} | |
png_init_io(png_ptr, fd); | |
color_depth = (3 == channels) ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY; | |
png_set_IHDR(png_ptr, info_ptr, WIDTH, height, 8, color_depth, | |
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, | |
PNG_FILTER_TYPE_DEFAULT); | |
png_write_info(png_ptr, info_ptr); | |
row_ptr = (png_bytep) safealloc(channels * WIDTH * sizeof(png_byte)); | |
/* Write the image line by line */ | |
for (row=0; row<height; row++) { | |
for (col=0; col<WIDTH; col++) { | |
block_num = col / 8; | |
block_offset = col % 8; | |
/* Channel buffer structure is not linear, find the offset | |
* corresponding to the required row/column coordinate */ | |
pixel_idx = (64*block_num + block_offset) + (((row % 8) + WIDTH * (row/8)) * 8); | |
/* Some channels may be shorter than others. In that case, just | |
* write black pixels to that channel */ | |
red_px = (pixel_idx >= red->len ? 0 : red->data[pixel_idx]); | |
if (3 == channels) { | |
green_px = (pixel_idx >= green->len ? 0 : green->data[pixel_idx]); | |
blue_px = (pixel_idx >= blue->len ? 0 : blue->data[pixel_idx]); | |
} | |
row_ptr[col*channels+0] = red_px; | |
if (3 == channels) { | |
row_ptr[col*3+1] = green_px; | |
row_ptr[col*3+2] = blue_px; | |
} | |
} | |
png_write_row(png_ptr, row_ptr); | |
} | |
/* Finalize the PNG and free the used buffer */ | |
png_write_end(png_ptr, info_ptr); | |
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); | |
png_destroy_write_struct(&png_ptr, NULL); | |
free(row_ptr); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment