scottie modes added ; with dayton paper copy

This commit is contained in:
2026-02-23 12:37:05 +01:00
parent ba38a51a68
commit 966c15cffb
9 changed files with 231 additions and 20 deletions

4
.gitignore vendored
View File

@@ -54,6 +54,10 @@ dkms.conf
# debug information files # debug information files
*.dwo *.dwo
## our application (binary)
sstv
e36
# special # special

BIN
Dayton Paper.pdf Normal file

Binary file not shown.

View File

@@ -2,8 +2,8 @@ CC = gcc
CFLAGS = -Wall -O2 CFLAGS = -Wall -O2
LDFLAGS = -lm LDFLAGS = -lm
TARGET = sstv TARGET = e36
SRCS = main.c sstv.c robot36.c image.c wav.c martin.c SRCS = main.c sstv.c robot36.c image.c wav.c martin.c scottie.c
OBJS = $(SRCS:.c=.o) OBJS = $(SRCS:.c=.o)
$(TARGET): $(OBJS) $(TARGET): $(OBJS)

View File

@@ -36,6 +36,6 @@ void image_free(image_t *img) {
void image_free_raw(image_t *img) { void image_free_raw(image_t *img) {
if (img->data) if (img->data)
free(img->data); free(img->data); // if you do this call only once, cause of double frees
img->data = NULL; img->data = NULL;
} }

53
main.c
View File

@@ -1,44 +1,69 @@
#include "sstv.h" #include "sstv.h"
#include "martin.h" #include "martin.h"
#include "scottie.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <time.h>
static void usage(const char *prog) { static void usage(const char *prog) {
printf("Usage: %s <mode> input_image output.wav\n", prog); fprintf(stderr,
printf("Modes:\n"); "Usage: %s <mode> <input_image> <output_wav>\n"
printf(" robot36 - Robot 36 (default)\n"); "\n"
printf(" martin1 - Martin M1\n"); "Modes:\n"
printf(" martin2 - Martin M2\n"); " robot36 Robot 36 (320x240)\n"
" martin1 Martin M1 (320x256)\n"
" martin2 Martin M2 (320x256)\n"
" scottie1 Scottie S1 (320x256)\n"
" scottie2 Scottie S2 (320x256)\n"
" scottiedx Scottie DX (320x256)\n"
"\n"
"Example:\n"
" %s mode photo.jpg output.wav\n",
prog, prog);
} }
int main(int argc, char **argv) { int main(int argc, char *argv[]) {
if (argc < 4) { if (argc != 4) {
usage(argv[0]); usage(argv[0]);
return 1; return 1;
} }
const char *mode_str = argv[1]; const char *mode = argv[1];
const char *input_img = argv[2]; const char *input_img = argv[2];
const char *output_wav = argv[3]; const char *output_wav = argv[3];
clock_t start = clock();
int ok = 0; int ok = 0;
if (strcmp(mode_str, "robot36") == 0) { if (strcmp(mode, "robot36") == 0) {
ok = sstv_encode_robot36(input_img, output_wav); ok = sstv_encode_robot36(input_img, output_wav);
} else if (strcmp(mode_str, "martin1") == 0) { } else if (strcmp(mode, "martin1") == 0) {
ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M1); ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M1);
} else if (strcmp(mode_str, "martin2") == 0) { } else if (strcmp(mode, "martin2") == 0) {
ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M2); ok = sstv_encode_martin(input_img, output_wav, &MARTIN_M2);
} else if (strcmp(mode, "scottie1") == 0) {
ok = sstv_encode_scottie(input_img, output_wav, &SCOTTIE_S1);
} else if (strcmp(mode, "scottie2") == 0) {
ok = sstv_encode_scottie(input_img, output_wav, &SCOTTIE_S2);
} else if (strcmp(mode, "scottiedx") == 0) {
ok = sstv_encode_scottie(input_img, output_wav, &SCOTTIE_DX);
} else { } else {
fprintf(stderr, "Unknown mode: %s\n", mode_str); fprintf(stderr, "unknown mode: %s\n\n", mode);
usage(argv[0]); usage(argv[0]);
return 1; return 1;
} }
if (!ok) { if (!ok) {
fprintf(stderr, "Encoding failed.\n"); fprintf(stderr, "make sure the file exsists");
return 1; return 1;
} }
printf("Done.\n"); clock_t end = clock();
double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
printf("encoded '%s' -> '%s' using '%s'\n", input_img, output_wav, mode);
printf("took %.3f seconds\n", elapsed);
return 0; return 0;
} }

View File

@@ -5,7 +5,7 @@
#include <stdint.h> #include <stdint.h>
typedef struct { typedef struct {
uint8_t vis_code; uint8_t vis_code; // in hexadecimal not normal decimal (ex 0x08)
int width; int width;
int height; int height;
double sync_ms; double sync_ms;

View File

@@ -1,12 +1,79 @@
# sstv e36 encoder # sstv e36 encoder
This is a simple C SSTV Robot36 encoder! This is a SSTV encoder, currently it is in work, but it has some modes already:
Written in pure C
|Support?|Mode |
|--------|------------|
|[x] |Martin 1 |
|[x] |Martin 2 |
|[x] |Robot 36 |
|[x] |Scottie 1 |
|[x] |Scottie 2 |
|[x] |ScottieD(S)X|
Written in _pure_ **C**
## License: ## License:
This project is distributed under the MiT License! This project is distributed under the MiT License!
### For developing or adding to the source code:
_**This is just standard C17 Standard**_
Compilation is done via the gcc compiler, we do NOT allow MSVC here.
If you are on Windows then i suggest to use the WSL.
Be sure to follow this style of code, it is not that complex,
and some parts you can even tell Vim or Emacs or whatever you use to automate it,
like the header definitions (default i think on vs and some others).
#### Headers:
Try to follow this general pattern.
In of itself it is relativley easy to maintain this structure.
And it is also relativley easy to write in this style!
_**This is just standard C17 Standard**_
```h
#ifndef HEADER_H
#define HEADER_H
#include <stdint.h>
typedef struct {
uint8_t type;
} mystruct_t;
int method_to_use(mystruct_t type);
#endif // !HEADER_H
```
#### C Files:
For a real implementation this stub below would make 0 sense,
but this is the general style you should follow, it includes CAPITAL definitions,
local imports in "" and at the top, others below.
Try to comment your code if it does not make sense.
If you encounter something like `Q_rsqrt` (The Quake inverse square root), then you are
permitted to comment some swear words.
Great example of commenting : "robot36.c"
```c
#include "header.h"
#define PI 3
#define SOME_DEF 67
int method_to_use(mystruct_t type) {
return 67 * type;
}
```
### Our License: ### Our License:
#### MIT : #### MIT :
``` ```
@@ -36,3 +103,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
``` ```

90
scottie.c Normal file
View File

@@ -0,0 +1,90 @@
#include "scottie.h"
#include "image.h"
#include "sstv.h"
#include "wav.h"
#include <stdint.h>
const scottie_mode_t SCOTTIE_S1 = {
.vis_code = 0x3C, // 60d
.width = 320,
.height = 256,
.separator_ms = 1.5,
.sync_ms = 9.0,
.sync_porch_ms = 1.5,
.scan_ms = 138.240,
};
const scottie_mode_t SCOTTIE_S2 = {
.vis_code = 0x38, // 56d
.width = 320,
.height = 256,
.separator_ms = 1.5,
.sync_ms = 9.0,
.sync_porch_ms = 1.5,
.scan_ms = 88.064,
};
const scottie_mode_t SCOTTIE_DX = {
.vis_code = 0x4C, // 76d
.width = 320,
.height = 256,
.separator_ms = 1.5,
.sync_ms = 9.0,
.sync_porch_ms = 1.5,
.scan_ms = 345.600,
};
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 scottie_encode_image(wav_t *wav, uint8_t *rgb, const scottie_mode_t *mode) {
for (int y = 0; y < mode->height; y++) {
uint8_t *line = &rgb[y * mode->width * 3];
if (y == 0) {
sstv_tone(wav, 1200.0, mode->sync_ms);
}
sstv_tone(wav, 1500.0, mode->separator_ms);
scan_line_channel(wav, line, mode->width, 1, mode->scan_ms);
sstv_tone(wav, 1500.0, mode->separator_ms);
scan_line_channel(wav, line, mode->width, 2, mode->scan_ms);
sstv_tone(wav, 1200.0, mode->sync_ms);
sstv_tone(wav, 1500.0, mode->sync_porch_ms);
scan_line_channel(wav, line, mode->width, 0, mode->scan_ms);
}
}
int sstv_encode_scottie(const char *input_image, const char *output_wav, const scottie_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);
scottie_encode_image(&wav, resized.data, mode);
wave_close(&wav);
image_free(&img);
image_free_raw(&resized);
return 1;
}

24
scottie.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef SCOTTIE_H
#define SCOTTIE_H
#include "wav.h"
#include <stdint.h>
typedef struct {
uint8_t vis_code;
int width;
int height;
double separator_ms;
double sync_ms;
double sync_porch_ms;
double scan_ms;
} scottie_mode_t;
extern const scottie_mode_t SCOTTIE_S1;
extern const scottie_mode_t SCOTTIE_S2;
extern const scottie_mode_t SCOTTIE_DX;
void scottie_encode_image(wav_t *wav, uint8_t *rgb, const scottie_mode_t *mode);
int sstv_encode_scottie(const char *input_image, const char *output_wav, const scottie_mode_t *mode);
#endif // !SCOTTIE_H