Skip to content

Instantly share code, notes, and snippets.

@hhrhhr
Last active August 14, 2019 23:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hhrhhr/0abb39b2fba297285bb16f291398d077 to your computer and use it in GitHub Desktop.
Save hhrhhr/0abb39b2fba297285bb16f291398d077 to your computer and use it in GitHub Desktop.
save 8-bit grayscale channels
#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;
}
}
}
}
}
/*}}}*/
#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