summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrimio <vasi.vilvoiu@gmail.com>2019-02-13 00:21:53 +0200
committerrimio <vasi.vilvoiu@gmail.com>2019-02-13 00:21:53 +0200
commit300fc3391381f76d7952d8f6d1135e24b5effe4b (patch)
treeaeea017e577bda8397338616f9a50df1aa89ca06
parent00ad11118c8c16c5b5d167bba04fea4d8113dd54 (diff)
Refactor: mode descriptor for timings and frequencies; LUT for pixel values
-rw-r--r--src/encoder.c151
-rw-r--r--src/sstv.c118
-rw-r--r--src/sstv.h58
3 files changed, 217 insertions, 110 deletions
diff --git a/src/encoder.c b/src/encoder.c
index 7d5655a..ff1aea6 100644
--- a/src/encoder.c
+++ b/src/encoder.c
@@ -10,40 +10,17 @@
#include "luts.h"
/*
- * Frequencies (all expressed in Hz)
+ * FSK helpers
*/
-#define LEADER_FREQ 1900
-#define BREAK_FREQ 1200
-
-#define VIS_START_STOP_FREQ 1200
-#define VIS_LOW_FREQ 1300
-#define VIS_HIGH_FREQ 1100
-
-#define SYNC_FREQ 1200
-#define PORCH_FREQ 1500
-
-#define DATA_BASE_FREQ 1500
-#define DATA_BANDWIDTH 800
-
-/*
- * Durations (useconds)
- */
-#define LEADER_TONE_USEC 300000
-#define BREAK_USEC 10000
-#define VIS_BIT_USEC 30000
-
-/*
- * Computation helpers
- */
-#define MICROSAMPLES_FROM_MICROSECONDS(time_us, sample_rate) ((uint64_t)(time_us) * (uint64_t)(sample_rate))
-#define DPHASE_FROM_FREQ(freq, sample_rate) ((((uint64_t)(freq)) << 32) / (uint64_t)(sample_rate))
-
-#define BYTE_TO_FREQ(b) (DATA_BASE_FREQ + (b) * DATA_BANDWIDTH / 255)
-
-#define FSK(ctx, usamples, freq) \
+#define FSK(ctx, time, freq) \
{ \
- (ctx)->fsk.phase_delta = DPHASE_FROM_FREQ((freq), context->sample_rate); \
- (ctx)->fsk.remaining_usamp += usamples; \
+ (ctx)->fsk.phase_delta = (freq).phase_delta; \
+ (ctx)->fsk.remaining_usamp += (time).usamp; \
+ }
+#define FSK_PIXEL(ctx, time, val) \
+ { \
+ (ctx)->fsk.phase_delta = (ctx)->descriptor.pixel.val_phase_delta[(val)]; \
+ (ctx)->fsk.remaining_usamp += (time).usamp; \
}
/*
@@ -100,11 +77,7 @@ typedef struct {
} fsk;
/* mode timings */
- struct {
- uint32_t sync_usamp;
- uint32_t porch_usamp;
- uint32_t pixel_usamp;
- } timings;
+ sstv_mode_descriptor_t descriptor;
/* state extra info */
union {
@@ -186,60 +159,14 @@ sstv_create_encoder(void **out_ctx, sstv_image_t image, sstv_mode_t mode, uint32
ctx->fsk.remaining_usamp = 0; /* so we get initial state change */
/* initialize mode timings */
- switch (mode) {
- /*
- * PD modes
- */
- case SSTV_MODE_PD50:
- ctx->timings.sync_usamp = MICROSAMPLES_FROM_MICROSECONDS(20000, sample_rate);
- ctx->timings.porch_usamp = MICROSAMPLES_FROM_MICROSECONDS(2080, sample_rate);
- ctx->timings.pixel_usamp = MICROSAMPLES_FROM_MICROSECONDS(286, sample_rate);
- break;
-
- case SSTV_MODE_PD90:
- ctx->timings.sync_usamp = MICROSAMPLES_FROM_MICROSECONDS(20000, sample_rate);
- ctx->timings.porch_usamp = MICROSAMPLES_FROM_MICROSECONDS(2080, sample_rate);
- ctx->timings.pixel_usamp = MICROSAMPLES_FROM_MICROSECONDS(532, sample_rate);
- break;
-
- case SSTV_MODE_PD120:
- ctx->timings.sync_usamp = MICROSAMPLES_FROM_MICROSECONDS(20000, sample_rate);
- ctx->timings.porch_usamp = MICROSAMPLES_FROM_MICROSECONDS(2080, sample_rate);
- ctx->timings.pixel_usamp = MICROSAMPLES_FROM_MICROSECONDS(190, sample_rate);
- break;
-
- case SSTV_MODE_PD160:
- ctx->timings.sync_usamp = MICROSAMPLES_FROM_MICROSECONDS(20000, sample_rate);
- ctx->timings.porch_usamp = MICROSAMPLES_FROM_MICROSECONDS(2080, sample_rate);
- ctx->timings.pixel_usamp = MICROSAMPLES_FROM_MICROSECONDS(382, sample_rate);
- break;
-
- case SSTV_MODE_PD180:
- ctx->timings.sync_usamp = MICROSAMPLES_FROM_MICROSECONDS(20000, sample_rate);
- ctx->timings.porch_usamp = MICROSAMPLES_FROM_MICROSECONDS(2080, sample_rate);
- ctx->timings.pixel_usamp = MICROSAMPLES_FROM_MICROSECONDS(286, sample_rate);
- break;
-
- case SSTV_MODE_PD240:
- ctx->timings.sync_usamp = MICROSAMPLES_FROM_MICROSECONDS(20000, sample_rate);
- ctx->timings.porch_usamp = MICROSAMPLES_FROM_MICROSECONDS(2080, sample_rate);
- ctx->timings.pixel_usamp = MICROSAMPLES_FROM_MICROSECONDS(382, sample_rate);
- break;
-
- case SSTV_MODE_PD290:
- ctx->timings.sync_usamp = MICROSAMPLES_FROM_MICROSECONDS(20000, sample_rate);
- ctx->timings.porch_usamp = MICROSAMPLES_FROM_MICROSECONDS(2080, sample_rate);
- ctx->timings.pixel_usamp = MICROSAMPLES_FROM_MICROSECONDS(286, sample_rate);
- break;
-
- /*
- * Invalid mode
- */
- default:
+ {
+ sstv_error_t rc = sstv_get_mode_descriptor(mode, sample_rate, &ctx->descriptor);
+ if (rc != SSTV_OK) {
if (sstv_free_user) {
sstv_free_user(ctx);
}
- return SSTV_BAD_MODE;
+ return rc;
+ }
}
/* set output */
@@ -286,7 +213,7 @@ sstv_encode_pd_state_change(sstv_encoder_context_t *context)
if (context->state == SSTV_ENCODER_STATE_VIS_STOP_BIT) {
context->state = SSTV_ENCODER_STATE_SYNC;
context->extra.scan.curr_line = 0;
- FSK(context, context->timings.sync_usamp, SYNC_FREQ);
+ FSK(context, context->descriptor.sync.time, context->descriptor.sync.freq);
return SSTV_OK;
}
@@ -297,14 +224,14 @@ sstv_encode_pd_state_change(sstv_encoder_context_t *context)
{
context->state = SSTV_ENCODER_STATE_SYNC;
context->extra.scan.curr_line += 2;
- FSK(context, context->timings.sync_usamp, SYNC_FREQ);
+ FSK(context, context->descriptor.sync.time, context->descriptor.sync.freq);
return SSTV_OK;
}
/* sync->porch */
if (context->state == SSTV_ENCODER_STATE_SYNC) {
context->state = SSTV_ENCODER_STATE_PORCH;
- FSK(context, context->timings.porch_usamp, PORCH_FREQ);
+ FSK(context, context->descriptor.porch.time, context->descriptor.porch.freq);
return SSTV_OK;
}
@@ -321,7 +248,7 @@ sstv_encode_pd_state_change(sstv_encoder_context_t *context)
uint32_t pix_offset = context->image.width * context->extra.scan.curr_line + context->extra.scan.curr_col;
uint8_t y = context->image.buffer[pix_offset * 3];
- FSK(context, context->timings.pixel_usamp, BYTE_TO_FREQ(y));
+ FSK_PIXEL(context, context->descriptor.pixel.time, y);
context->extra.scan.curr_col ++;
return SSTV_OK;
@@ -343,7 +270,7 @@ sstv_encode_pd_state_change(sstv_encoder_context_t *context)
uint8_t r1 = context->image.buffer[pix_offset_l0 * 3 + 2];
uint8_t r2 = context->image.buffer[pix_offset_l1 * 3 + 2];
uint8_t r = (r1 + r2) / 2;
- FSK(context, context->timings.pixel_usamp, BYTE_TO_FREQ(r));
+ FSK_PIXEL(context, context->descriptor.pixel.time, r);
context->extra.scan.curr_col ++;
return SSTV_OK;
@@ -365,7 +292,7 @@ sstv_encode_pd_state_change(sstv_encoder_context_t *context)
uint8_t b1 = context->image.buffer[pix_offset_l0 * 3 + 1];
uint8_t b2 = context->image.buffer[pix_offset_l1 * 3 + 1];
uint8_t b = (b1 + b2) / 2;
- FSK(context, context->timings.pixel_usamp, BYTE_TO_FREQ(b));
+ FSK_PIXEL(context, context->descriptor.pixel.time, b);
context->extra.scan.curr_col ++;
return SSTV_OK;
@@ -384,7 +311,7 @@ sstv_encode_pd_state_change(sstv_encoder_context_t *context)
uint32_t pix_offset = context->image.width * (context->extra.scan.curr_line + 1) + context->extra.scan.curr_col;
uint8_t y = context->image.buffer[pix_offset * 3];
- FSK(context, context->timings.pixel_usamp, BYTE_TO_FREQ(y));
+ FSK_PIXEL(context, context->descriptor.pixel.time, y);
context->extra.scan.curr_col ++;
return SSTV_OK;
@@ -402,8 +329,8 @@ sstv_encode_state_change(sstv_encoder_context_t *context)
if (context->state == SSTV_ENCODER_STATE_START) {
context->state = SSTV_ENCODER_STATE_LEADER_TONE_1;
FSK(context,
- MICROSAMPLES_FROM_MICROSECONDS(LEADER_TONE_USEC, context->sample_rate),
- LEADER_FREQ);
+ context->descriptor.leader_tone.time,
+ context->descriptor.leader_tone.freq);
return SSTV_OK;
}
@@ -411,8 +338,8 @@ sstv_encode_state_change(sstv_encoder_context_t *context)
if (context->state == SSTV_ENCODER_STATE_LEADER_TONE_1) {
context->state = SSTV_ENCODER_STATE_BREAK;
FSK(context,
- MICROSAMPLES_FROM_MICROSECONDS(BREAK_USEC, context->sample_rate),
- BREAK_FREQ);
+ context->descriptor.break_tone.time,
+ context->descriptor.break_tone.freq);
return SSTV_OK;
}
@@ -420,19 +347,19 @@ sstv_encode_state_change(sstv_encoder_context_t *context)
if (context->state == SSTV_ENCODER_STATE_BREAK) {
context->state = SSTV_ENCODER_STATE_LEADER_TONE_2;
FSK(context,
- MICROSAMPLES_FROM_MICROSECONDS(LEADER_TONE_USEC, context->sample_rate),
- LEADER_FREQ);
+ context->descriptor.leader_tone.time,
+ context->descriptor.leader_tone.freq);
return SSTV_OK;
}
/* VIS start bit */
if (context->state == SSTV_ENCODER_STATE_LEADER_TONE_2) {
context->state = SSTV_ENCODER_STATE_VIS_START_BIT;
- FSK(context,
- MICROSAMPLES_FROM_MICROSECONDS(VIS_BIT_USEC, context->sample_rate),
- VIS_START_STOP_FREQ);
context->extra.vis.visp = sstv_get_visp_code(context->mode);
context->extra.vis.curr_bit = 0;
+ FSK(context,
+ context->descriptor.vis.time,
+ context->descriptor.vis.sep_freq);
return SSTV_OK;
}
@@ -443,9 +370,15 @@ sstv_encode_state_change(sstv_encoder_context_t *context)
uint8_t bit = (context->extra.vis.visp >> context->extra.vis.curr_bit) & 0x1;
context->state = SSTV_ENCODER_STATE_VIS_BIT;
context->extra.vis.curr_bit ++;
- FSK(context,
- MICROSAMPLES_FROM_MICROSECONDS(VIS_BIT_USEC, context->sample_rate),
- (bit ? VIS_HIGH_FREQ : VIS_LOW_FREQ));
+ if (bit) {
+ FSK(context,
+ context->descriptor.vis.time,
+ context->descriptor.vis.high_freq);
+ } else {
+ FSK(context,
+ context->descriptor.vis.time,
+ context->descriptor.vis.low_freq);
+ }
return SSTV_OK;
}
@@ -453,8 +386,8 @@ sstv_encode_state_change(sstv_encoder_context_t *context)
if (context->state == SSTV_ENCODER_STATE_VIS_BIT) {
context->state = SSTV_ENCODER_STATE_VIS_STOP_BIT;
FSK(context,
- MICROSAMPLES_FROM_MICROSECONDS(VIS_BIT_USEC, context->sample_rate),
- VIS_START_STOP_FREQ);
+ context->descriptor.vis.time,
+ context->descriptor.vis.sep_freq);
return SSTV_OK;
}
diff --git a/src/sstv.c b/src/sstv.c
index b4ff28e..22c7654 100644
--- a/src/sstv.c
+++ b/src/sstv.c
@@ -22,6 +22,12 @@
#define CYCbCr2B(Y, Cb, Cr) CLIP( Y + (116129 * Cb >> 16 ) - 226 )
/*
+ * Timings macros
+ */
+#define TIME_DESC_INIT(time_us, sample_rate) ((sstv_timing_desc_t){ (time_us), ((uint64_t)(time_us) * (uint64_t)(sample_rate)) })
+#define FREQ_DESC_INIT(freq, sample_rate) ((sstv_freq_desc_t){ (freq), ((((uint64_t)(freq)) << 32) / (uint64_t)(sample_rate)) })
+
+/*
* User-defined memory allocation functions
*/
sstv_malloc_t sstv_malloc_user = NULL;
@@ -363,4 +369,116 @@ sstv_get_visp_code(sstv_mode_t mode)
default:
return 0;
}
+}
+
+sstv_error_t
+sstv_get_mode_descriptor(sstv_mode_t mode, uint32_t sample_rate, sstv_mode_descriptor_t *desc)
+{
+ if (!desc) {
+ return SSTV_INTERNAL_ERROR;
+ }
+
+ /* Common desc and frequencies */
+ desc->leader_tone.time = TIME_DESC_INIT(300000, sample_rate);
+ desc->leader_tone.freq = FREQ_DESC_INIT(1900, sample_rate);
+
+ desc->break_tone.time = TIME_DESC_INIT(10000, sample_rate);
+ desc->break_tone.freq = FREQ_DESC_INIT(1200, sample_rate);
+
+ desc->vis.time = TIME_DESC_INIT(30000, sample_rate);
+ desc->vis.sep_freq = FREQ_DESC_INIT(1200, sample_rate);
+ desc->vis.low_freq = FREQ_DESC_INIT(1300, sample_rate);
+ desc->vis.high_freq = FREQ_DESC_INIT(1100, sample_rate);
+
+ /* Mode frequencies */
+ switch (mode) {
+ /*
+ * PD modes
+ */
+ case SSTV_MODE_PD50:
+ case SSTV_MODE_PD90:
+ case SSTV_MODE_PD120:
+ case SSTV_MODE_PD160:
+ case SSTV_MODE_PD180:
+ case SSTV_MODE_PD240:
+ case SSTV_MODE_PD290:
+ desc->sync.freq = FREQ_DESC_INIT(1200, sample_rate);
+ desc->porch.freq = FREQ_DESC_INIT(1500, sample_rate);
+ desc->pixel.low_freq = FREQ_DESC_INIT(1500, sample_rate);
+ desc->pixel.bandwidth = FREQ_DESC_INIT(800, sample_rate);
+ break;
+
+ /*
+ * invalid mode
+ */
+ default:
+ return SSTV_BAD_MODE;
+ }
+
+ /* Mode desc */
+ switch (mode) {
+ /*
+ * PD modes
+ */
+ case SSTV_MODE_PD50:
+ desc->sync.time = TIME_DESC_INIT(20000, sample_rate);
+ desc->porch.time = TIME_DESC_INIT(2080, sample_rate);
+ desc->pixel.time = TIME_DESC_INIT(286, sample_rate);
+ break;
+
+ case SSTV_MODE_PD90:
+ desc->sync.time = TIME_DESC_INIT(20000, sample_rate);
+ desc->porch.time = TIME_DESC_INIT(2080, sample_rate);
+ desc->pixel.time = TIME_DESC_INIT(532, sample_rate);
+ break;
+
+ case SSTV_MODE_PD120:
+ desc->sync.time = TIME_DESC_INIT(20000, sample_rate);
+ desc->porch.time = TIME_DESC_INIT(2080, sample_rate);
+ desc->pixel.time = TIME_DESC_INIT(190, sample_rate);
+ break;
+
+ case SSTV_MODE_PD160:
+ desc->sync.time = TIME_DESC_INIT(20000, sample_rate);
+ desc->porch.time = TIME_DESC_INIT(2080, sample_rate);
+ desc->pixel.time = TIME_DESC_INIT(382, sample_rate);
+ break;
+
+ case SSTV_MODE_PD180:
+ desc->sync.time = TIME_DESC_INIT(20000, sample_rate);
+ desc->porch.time = TIME_DESC_INIT(2080, sample_rate);
+ desc->pixel.time = TIME_DESC_INIT(286, sample_rate);
+ break;
+
+ case SSTV_MODE_PD240:
+ desc->sync.time = TIME_DESC_INIT(20000, sample_rate);
+ desc->porch.time = TIME_DESC_INIT(2080, sample_rate);
+ desc->pixel.time = TIME_DESC_INIT(382, sample_rate);
+ break;
+
+ case SSTV_MODE_PD290:
+ desc->sync.time = TIME_DESC_INIT(20000, sample_rate);
+ desc->porch.time = TIME_DESC_INIT(2080, sample_rate);
+ desc->pixel.time = TIME_DESC_INIT(286, sample_rate);
+ break;
+
+ /*
+ * invalid mode
+ */
+ default:
+ return SSTV_BAD_MODE;
+ }
+
+ /* compute pixel value to delta phase lookup table */
+ for (uint32_t i = 0; i < 256; i ++) {
+ uint64_t freq_t255 =
+ (uint64_t)desc->pixel.low_freq.hz * 255 + ((uint64_t)desc->pixel.bandwidth.hz * i);
+
+ uint64_t dphase = (freq_t255 << 32) / ((uint64_t)(sample_rate) * 255);
+
+ desc->pixel.val_phase_delta[i] = (uint32_t) dphase;
+ }
+
+ /* all ok */
+ return SSTV_OK;
} \ No newline at end of file
diff --git a/src/sstv.h b/src/sstv.h
index f36969d..c056381 100644
--- a/src/sstv.h
+++ b/src/sstv.h
@@ -11,7 +11,60 @@
#include "libsstv.h"
/*
- * Memory management functions
+ * Mode timings and frequency descriptors
+ */
+typedef struct {
+ uint32_t hz;
+ uint32_t phase_delta;
+} sstv_freq_desc_t;
+
+typedef struct {
+ uint32_t usec;
+ uint32_t usamp;
+} sstv_timing_desc_t;
+
+typedef struct {
+ /* Header */
+ struct {
+ sstv_timing_desc_t time;
+ sstv_freq_desc_t freq;
+ } leader_tone;
+
+ struct {
+ sstv_timing_desc_t time;
+ sstv_freq_desc_t freq;
+ } break_tone;
+
+ struct {
+ sstv_timing_desc_t time;
+ sstv_freq_desc_t sep_freq;
+ sstv_freq_desc_t low_freq;
+ sstv_freq_desc_t high_freq;
+ } vis;
+
+ /* Mode */
+ struct {
+ sstv_timing_desc_t time;
+ sstv_freq_desc_t freq;
+ } sync;
+
+ struct {
+ sstv_timing_desc_t time;
+ sstv_freq_desc_t freq;
+ } porch;
+
+ struct {
+ sstv_timing_desc_t time;
+ sstv_freq_desc_t low_freq;
+ sstv_freq_desc_t bandwidth;
+
+ /* lookup table from value to delta-phase */
+ uint32_t val_phase_delta[256];
+ } pixel;
+} sstv_mode_descriptor_t;
+
+/*
+ * Memory management
*/
extern sstv_malloc_t sstv_malloc_user;
extern sstv_free_t sstv_free_user;
@@ -22,4 +75,7 @@ extern sstv_free_t sstv_free_user;
extern uint8_t
sstv_get_visp_code(sstv_mode_t mode);
+extern sstv_error_t
+sstv_get_mode_descriptor(sstv_mode_t mode, uint32_t sample_rate, sstv_mode_descriptor_t *desc);
+
#endif \ No newline at end of file