martin modes, and some bugfixes to get them working
This commit is contained in:
2
Makefile
2
Makefile
@@ -3,7 +3,7 @@ CFLAGS = -Wall -O2
|
|||||||
LDFLAGS = -lm
|
LDFLAGS = -lm
|
||||||
|
|
||||||
TARGET = sstv
|
TARGET = sstv
|
||||||
SRCS = main.c sstv.c robot36.c image.c wav.c
|
SRCS = main.c sstv.c robot36.c image.c wav.c martin.c
|
||||||
OBJS = $(SRCS:.c=.o)
|
OBJS = $(SRCS:.c=.o)
|
||||||
|
|
||||||
$(TARGET): $(OBJS)
|
$(TARGET): $(OBJS)
|
||||||
|
|||||||
35
main.c
35
main.c
@@ -1,14 +1,41 @@
|
|||||||
#include "sstv.h"
|
#include "sstv.h"
|
||||||
|
#include "martin.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void usage(const char *prog) {
|
||||||
|
printf("Usage: %s <mode> input_image output.wav\n", prog);
|
||||||
|
printf("Modes:\n");
|
||||||
|
printf(" robot36 - Robot 36 (default)\n");
|
||||||
|
printf(" martin1 - Martin M1\n");
|
||||||
|
printf(" martin2 - Martin M2\n");
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
if (argc < 3) {
|
if (argc < 4) {
|
||||||
printf("Usage: %s input_image output.wav\n", argv[0]);
|
usage(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sstv_encode_robot36(argv[1], argv[2])) {
|
const char *mode_str = argv[1];
|
||||||
printf("Encoding failed\n");
|
const char *input_img = argv[2];
|
||||||
|
const char *output_wav = argv[3];
|
||||||
|
int ok = 0;
|
||||||
|
|
||||||
|
if (strcmp(mode_str, "robot36") == 0) {
|
||||||
|
ok = sstv_encode_robot36(input_img, output_wav);
|
||||||
|
} else if (strcmp(mode_str, "martin1") == 0) {
|
||||||
|
ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M1);
|
||||||
|
} else if (strcmp(mode_str, "martin2") == 0) {
|
||||||
|
ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M2);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unknown mode: %s\n", mode_str);
|
||||||
|
usage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
fprintf(stderr, "Encoding failed.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
78
martin.c
Normal file
78
martin.c
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#include "martin.h"
|
||||||
|
#include "image.h"
|
||||||
|
#include "sstv.h"
|
||||||
|
#include "wav.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
const martin_mode_t MARTIN_M1 = {
|
||||||
|
.vis_code = 0x2C, // hex (dd -> 44d)
|
||||||
|
.width = 320,
|
||||||
|
.height = 256,
|
||||||
|
.sync_ms = 4.862,
|
||||||
|
.sync_porch_ms = 0.572,
|
||||||
|
.separator_ms = 0.572,
|
||||||
|
.scan_ms = 146.432, // per channel , per line
|
||||||
|
};
|
||||||
|
|
||||||
|
const martin_mode_t MARTIN_M2 = {
|
||||||
|
.vis_code = 0x28, // hex (dd -> 40d)
|
||||||
|
.width = 320,
|
||||||
|
.height = 256,
|
||||||
|
.sync_ms = 4.862,
|
||||||
|
.sync_porch_ms = 0.572,
|
||||||
|
.separator_ms = 0.572,
|
||||||
|
.scan_ms = 73.216, // half of M1
|
||||||
|
};
|
||||||
|
|
||||||
|
static void scan_line_channel(wav_t *wav, uint8_t *line, int width, int channel, double scan_ms) {
|
||||||
|
double px_ms = scan_ms / width;
|
||||||
|
for (int x = 0; x < width; x++) {
|
||||||
|
uint8_t val = line[x * 3 + channel];
|
||||||
|
double freq = 1500.0 + val * 3.1372549;
|
||||||
|
sstv_tone(wav, freq, px_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void martin_encode_image(wav_t *wav, uint8_t *rgb, const martin_mode_t *mode) {
|
||||||
|
for (int y = 0; y < mode->height; y++) {
|
||||||
|
uint8_t *line = &rgb[y * mode->width * 3];
|
||||||
|
|
||||||
|
sstv_tone(wav, 1200.0, mode->sync_ms); // sync pulse
|
||||||
|
sstv_tone(wav, 1500.0, mode->sync_porch_ms); // sync porch
|
||||||
|
scan_line_channel(wav, line, mode->width, 1, mode->scan_ms); // G
|
||||||
|
sstv_tone(wav, 1500.0, mode->separator_ms);
|
||||||
|
scan_line_channel(wav, line, mode->width, 2, mode->scan_ms); // B
|
||||||
|
sstv_tone(wav, 1500.0, mode->separator_ms);
|
||||||
|
scan_line_channel(wav, line, mode->width, 0, mode->scan_ms); // R
|
||||||
|
sstv_tone(wav, 1500.0, mode->separator_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int sstv_encode_martin(const char *input_image, const char *output_wav, const martin_mode_t *mode) {
|
||||||
|
image_t img, resized;
|
||||||
|
wav_t wav;
|
||||||
|
|
||||||
|
if (!image_load(&img, input_image)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!image_resize(&img, &resized, mode->width, mode->height)) {
|
||||||
|
image_free(&img);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wave_open(&wav, output_wav, 44100)) {
|
||||||
|
image_free(&img);
|
||||||
|
image_free_raw(&resized);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sstv_vis_header_ex(&wav, mode->vis_code);
|
||||||
|
martin_encode_image(&wav, resized.data, mode);
|
||||||
|
wave_close(&wav);
|
||||||
|
|
||||||
|
image_free(&img);
|
||||||
|
image_free_raw(&resized);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
23
martin.h
Normal file
23
martin.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef MARTIN_H
|
||||||
|
#define MARTIN_H
|
||||||
|
|
||||||
|
#include "wav.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t vis_code;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
double sync_ms;
|
||||||
|
double sync_porch_ms;
|
||||||
|
double separator_ms;
|
||||||
|
double scan_ms;
|
||||||
|
} martin_mode_t;
|
||||||
|
|
||||||
|
extern const martin_mode_t MARTIN_M1;
|
||||||
|
extern const martin_mode_t MARTIN_M2;
|
||||||
|
|
||||||
|
void martin_encode_image(wav_t *wav, uint8_t *rgb, const martin_mode_t *mode);
|
||||||
|
int sstv_encode_martin(const char *input_image, const char *output_wav, const martin_mode_t *mode);
|
||||||
|
|
||||||
|
#endif // !MARTIN_H
|
||||||
38
sstv.c
38
sstv.c
@@ -5,16 +5,19 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define PI 3.14159265358979
|
#define PI 3.14159265358979
|
||||||
#define AMPLITUDE 12000
|
#define AMPLITUDE 12000
|
||||||
|
|
||||||
static double phase = 0.0;
|
static double phase = 0.0;
|
||||||
|
static double sample_accum = 0.0; // accumulator
|
||||||
|
|
||||||
void sstv_tone(wav_t *wav, double freq, double duration_ms) {
|
void sstv_tone(wav_t *wav, double freq, double duration_ms) {
|
||||||
uint32_t total_samples = (uint32_t)((duration_ms / 1000.0) * wav->sample_rate);
|
sample_accum += (duration_ms / 1000.0) * wav->sample_rate;
|
||||||
|
uint32_t total_samples = (uint32_t)sample_accum;
|
||||||
|
sample_accum -= total_samples;
|
||||||
|
|
||||||
|
double step = 2.0 * PI * freq / wav->sample_rate;
|
||||||
for (uint32_t i = 0; i < total_samples; i++) {
|
for (uint32_t i = 0; i < total_samples; i++) {
|
||||||
double step = 2.0 * PI * freq / wav->sample_rate;
|
|
||||||
phase += step;
|
phase += step;
|
||||||
if (phase > 2.0 * PI)
|
if (phase > 2.0 * PI)
|
||||||
phase -= 2.0 * PI;
|
phase -= 2.0 * PI;
|
||||||
@@ -24,31 +27,36 @@ void sstv_tone(wav_t *wav, double freq, double duration_ms) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sstv_vis_header(wav_t *wav) {
|
void sstv_vis_header_ex(wav_t *wav, uint8_t vis_code) {
|
||||||
uint8_t vis = 0x08;
|
// reset accumulator
|
||||||
uint8_t parity = 0;
|
phase = 0.0;
|
||||||
|
sample_accum = 0.0;
|
||||||
|
|
||||||
|
/* preamble */
|
||||||
sstv_tone(wav, 1900, 300);
|
sstv_tone(wav, 1900, 300);
|
||||||
sstv_tone(wav, 1200, 10);
|
sstv_tone(wav, 1200, 10);
|
||||||
sstv_tone(wav, 1900, 300);
|
sstv_tone(wav, 1900, 300);
|
||||||
|
|
||||||
sstv_tone(wav, 1200, 30);
|
sstv_tone(wav, 1200, 30);
|
||||||
|
|
||||||
|
uint8_t parity = 0;
|
||||||
for (int i = 0; i < 7; i++) {
|
for (int i = 0; i < 7; i++) {
|
||||||
uint8_t bit = (vis >> i) & 1;
|
uint8_t bit = (vis_code >> i) & 1;
|
||||||
if (bit) {
|
sstv_tone(wav, bit ? 1100 : 1300, 30);
|
||||||
sstv_tone(wav, 1100, 30);
|
parity ^= bit;
|
||||||
parity ^= 1;
|
|
||||||
} else {
|
|
||||||
sstv_tone(wav, 1300, 30);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* parity bit */
|
||||||
sstv_tone(wav, parity ? 1100 : 1300, 30);
|
sstv_tone(wav, parity ? 1100 : 1300, 30);
|
||||||
|
|
||||||
|
/* stop bit */
|
||||||
sstv_tone(wav, 1200, 30);
|
sstv_tone(wav, 1200, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sstv_vis_header(wav_t *wav) {
|
||||||
|
sstv_vis_header_ex(wav, 0x08);
|
||||||
|
}
|
||||||
|
|
||||||
int sstv_encode_robot36(const char *input_image, const char *output_wav) {
|
int sstv_encode_robot36(const char *input_image, const char *output_wav) {
|
||||||
image_t img, resized;
|
image_t img, resized;
|
||||||
wav_t wav;
|
wav_t wav;
|
||||||
|
|||||||
6
sstv.h
6
sstv.h
@@ -5,7 +5,11 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void sstv_tone(wav_t *wav, double freq, double duration_ms);
|
void sstv_tone(wav_t *wav, double freq, double duration_ms);
|
||||||
|
|
||||||
void sstv_vis_header(wav_t *wav);
|
void sstv_vis_header(wav_t *wav);
|
||||||
int sstv_encode_robot36(const char *input_image, const char *output_wav);
|
|
||||||
|
void sstv_vis_header_ex(wav_t *wav, uint8_t vis_code);
|
||||||
|
|
||||||
|
int sstv_encode_robot36(const char *input_image, const char *output_wav);
|
||||||
|
|
||||||
#endif // !SSTV_H
|
#endif // !SSTV_H
|
||||||
|
|||||||
Reference in New Issue
Block a user