libsstvenc
Asynchronous Analogue SSTV encoder
Loading...
Searching...
No Matches
cw.c
Go to the documentation of this file.
1/*
2 * © Stuart Longland VK4MSL
3 * SPDX-License-Identifier: MIT
4 */
5
6#include <libsstvenc/cw.h>
7#include <string.h>
8
19 const char* key;
20 const char* value;
21};
22
34 /* Whitespace */
35 {.key = " ", .value = " "}, /* NB: some space is already added */
36 /* Letters */
37 {.key = "A", .value = ".-"},
38 {.key = "B", .value = "-..."},
39 {.key = "C", .value = "-.-."},
40 {.key = "D", .value = "-.."},
41 {.key = "E", .value = "."},
42 {.key = "F", .value = "..-."},
43 {.key = "G", .value = "--."},
44 {.key = "H", .value = "...."},
45 {.key = "I", .value = ".."},
46 {.key = "J", .value = ".---"},
47 {.key = "K", .value = "-.-"},
48 {.key = "L", .value = ".-.."},
49 {.key = "M", .value = "--"},
50 {.key = "N", .value = "-."},
51 {.key = "O", .value = "---"},
52 {.key = "P", .value = ".--."},
53 {.key = "Q", .value = "--.-"},
54 {.key = "R", .value = ".-."},
55 {.key = "S", .value = "..."},
56 {.key = "T", .value = "-"},
57 {.key = "U", .value = "..-"},
58 {.key = "V", .value = "...-"},
59 {.key = "W", .value = ".--"},
60 {.key = "X", .value = "-..-"},
61 {.key = "Y", .value = "-.--"},
62 {.key = "Z", .value = "--.."},
63 /* Digits */
64 {.key = "0", .value = "-----"},
65 {.key = "1", .value = ".----"},
66 {.key = "2", .value = "..---"},
67 {.key = "3", .value = "...--"},
68 {.key = "4", .value = "....-"},
69 {.key = "5", .value = "....."},
70 {.key = "6", .value = "-...."},
71 {.key = "7", .value = "--..."},
72 {.key = "8", .value = "---.."},
73 {.key = "9", .value = "----."},
74 /* Symbols */
75 {.key = ".", .value = ".-.-.-"},
76 {.key = ",", .value = "--..--"},
77 {.key = "?", .value = "..--.."},
78 {.key = "'", .value = ".----."},
79 {.key = "!", .value = "-.-.--"},
80 {.key = "/", .value = "-..-."},
81 {.key = "(", .value = "-.--."},
82 {.key = ")", .value = "-.--.-"},
83 {.key = "&", .value = ".-..."},
84 {.key = ":", .value = "---..."},
85 {.key = "=", .value = "-...-"},
86 {.key = "+", .value = ".-.-."},
87 {.key = "-", .value = "-....-"},
88 {.key = "_", .value = "..--.-"},
89 {.key = "\"", .value = ".-..-."},
90 {.key = "$", .value = "...-..-"},
91 {.key = "@", .value = ".--.-."},
92 {.key = NULL, .value = NULL},
93};
94
100 /* Non-English */
101 {.key = "À", .value = ".--.-"}, /* also Å */
102 {.key = "Ä", .value = ".-.-"}, /* also Æ Ą */
103 {.key = "Å", .value = ".--.-"},
104 {.key = "Æ", .value = ".-.-"},
105 {.key = "Ą", .value = ".-.-"},
106 {.key = "Ć", .value = "-.-.."}, /* also Ĉ Ç */
107 {.key = "Ĉ", .value = "-.-.."},
108 {.key = "Ç", .value = "-.-.."},
109 {.key = "Ð", .value = "..--."},
110 {.key = "É", .value = "..-.."}, /* also Ę */
111 {.key = "È", .value = ".-..-"}, /* also Ł */
112 {.key = "Ę", .value = "..-.."},
113 {.key = "Ĝ", .value = "--.-."},
114 {.key = "Ĥ", .value = "----"}, /* also <CH> Š */
115 {.key = "Ĵ", .value = ".---."},
116 {.key = "Ł", .value = ".-..-"},
117 {.key = "Ń", .value = "--.--"}, /* also Ñ */
118 {.key = "Ñ", .value = "--.--"},
119 {.key = "Ó", .value = "---."}, /* also Ö Ø */
120 {.key = "Ö", .value = "---."},
121 {.key = "Ø", .value = "---."},
122 {.key = "Ś", .value = "...-..."},
123 {.key = "Ŝ", .value = "...-."},
124 {.key = "Š", .value = "----"},
125 {.key = "Þ", .value = ".--.."},
126 {.key = "Ü", .value = "..--"}, /* also Ŭ */
127 {.key = "Ŭ", .value = "..--"},
128 {.key = "Ź", .value = "--..-."},
129 {.key = "Ż", .value = "--..-"},
130 {.key = "<CH>", .value = "----"},
131 /*
132 * Prosigns: since < and > are not valid, we use these to
133 * define the start and end of a prosign name.
134 */
135 {.key = "<END_OF_WORK>", .value = "...-.-"},
136 {.key = "<ERROR>", .value = "........"},
137 {.key = "<INVITATION>", .value = "-.-"},
138 {.key = "<START>", .value = "-.-.-"},
139 {.key = "<NEW_MESSAGE>", .value = ".-.-."},
140 {.key = "<VERIFIED>", .value = "...-."},
141 {.key = "<WAIT>", .value = ".-..."},
142 {.key = NULL, .value = NULL},
143};
144
159static const struct sstvenc_cw_pair*
160sstvenc_cw_symbol_match(const char* sym,
161 const struct sstvenc_cw_pair* candidate);
162
190static const struct sstvenc_cw_pair*
191sstvenc_cw_symbol_lookup(const char* sym, const struct sstvenc_cw_pair* table,
192 uint8_t len);
193
210static const struct sstvenc_cw_pair* sstvenc_cw_get_symbol(const char* sym);
211
226static void sstvenc_cw_get_next_sym(struct sstvenc_cw_mod* const cw);
227
243static void sstvenc_cw_start_mark(struct sstvenc_cw_mod* const cw);
244
251static void sstvenc_cw_end_subsym(struct sstvenc_cw_mod* const cw);
252
261static void sstvenc_cw_end_symbol(struct sstvenc_cw_mod* const cw);
262
277static void sstvenc_cw_handle_state_mark(struct sstvenc_cw_mod* const cw);
278
289static void sstvenc_cw_handle_state_ditspace(struct sstvenc_cw_mod* const cw);
290
304static void sstvenc_cw_handle_state_dahspace(struct sstvenc_cw_mod* const cw);
305
310static void sstvenc_cw_handle_state_done(struct sstvenc_cw_mod* const cw);
311
312void sstvenc_cw_init(struct sstvenc_cw_mod* const cw, const char* text,
313 double amplitude, double frequency, double dit_period,
314 double slope_period, uint32_t sample_rate,
315 uint8_t time_unit) {
316
317 sstvenc_ps_init(&(cw->ps), amplitude, slope_period, INFINITY,
318 slope_period, sample_rate, time_unit);
319 sstvenc_osc_init(&(cw->osc), 1.0, frequency, 0.0, sample_rate);
320 cw->dit_period
321 = sstvenc_ts_unit_to_samples(dit_period, sample_rate, time_unit);
322 cw->pos = 0;
323 cw->symbol = NULL;
324 cw->text_string = text;
326}
327
328static const struct sstvenc_cw_pair*
330 const struct sstvenc_cw_pair* candidate) {
331 /* Check match */
332 if (!strncmp(sym, candidate->key, strlen(candidate->key))) {
333 /* Return the match */
334 return candidate;
335 } else {
336 /* Not a match */
337 return NULL;
338 }
339}
340
341static const struct sstvenc_cw_pair*
342sstvenc_cw_symbol_lookup(const char* sym, const struct sstvenc_cw_pair* table,
343 uint8_t len) {
344 while (table->key) {
345 if (len) {
346 /* Known length, compare the symbols */
347 if (!strncmp(sym, table->key, len)
348 && (table->key[len] == 0)) {
349 /* Return the match */
350 return table;
351 }
352 } else {
353 const struct sstvenc_cw_pair* match
354 = sstvenc_cw_symbol_match(sym, table);
355 if (match) {
356 return match;
357 }
358 }
359 table++;
360 }
361
362 return NULL;
363}
364
365static const struct sstvenc_cw_pair* sstvenc_cw_get_symbol(const char* sym) {
366 /* Short-cut, look for single character symbols first */
367 const struct sstvenc_cw_pair* match
369 if (match) {
370 return match;
371 }
372
373 /* Try searching the multi-byte table */
375 if (match) {
376 return match;
377 }
378
379 /* Nothing found */
380 return NULL;
381}
382
383static void sstvenc_cw_get_next_sym(struct sstvenc_cw_mod* const cw) {
384 while ((cw->symbol == NULL) && cw->text_string[0]) {
385 /* Look up the next symbol in the string */
387 if (cw->symbol == NULL) {
388 /* Nothing here, advance to the next position */
389 cw->text_string++;
390 }
391 }
392
393 if (cw->symbol) {
394 /* We have a character, reset the position */
396 cw->pos = 0;
397
398 /* Process the mark so we have a valid output sample */
400 } else {
403 }
404}
405
406static void sstvenc_cw_start_mark(struct sstvenc_cw_mod* const cw) {
407 switch (cw->symbol->value[cw->pos]) {
408 case ' ':
409 /* A space */
410 cw->osc.amplitude = 0.0;
412 - cw->ps.rise_sz
413 - cw->ps.fall_sz);
414 break;
415 case '.':
416 /* A dit */
417 cw->osc.amplitude = 1.0;
419 - cw->ps.rise_sz
420 - cw->ps.fall_sz);
421 break;
422 case '-':
423 /* A dah */
424 cw->osc.amplitude = 1.0;
425 sstvenc_ps_reset_samples(&(cw->ps), (cw->dit_period * 3)
426 - cw->ps.rise_sz
427 - cw->ps.fall_sz);
428 break;
429 }
430}
431
432static void sstvenc_cw_end_subsym(struct sstvenc_cw_mod* const cw) {
434 cw->pos++;
435 if (cw->symbol->value[cw->pos]) {
436 /* We need to produce another mark */
439 } else {
440 /* End of the symbol */
443 }
444}
445
446static void sstvenc_cw_end_symbol(struct sstvenc_cw_mod* const cw) {
448 cw->text_string += strlen(cw->symbol->key);
449 cw->symbol = NULL;
452}
453
454static void sstvenc_cw_handle_state_mark(struct sstvenc_cw_mod* const cw) {
455 switch (cw->ps.phase) {
457 /* Start of a dah/dit/space */
459 /* Fall thru */
463 /* Dah/Dit in progress */
464 sstvenc_ps_compute(&(cw->ps));
465 sstvenc_osc_compute(&(cw->osc));
466 cw->output = cw->ps.output * cw->osc.output;
467 break;
469 /* Dah/Dit finished, space starts here */
470 cw->output = 0.0;
471 cw->osc.amplitude = 0.0;
472 if (cw->symbol->value[cw->pos] == ' ') {
473 /* This was actually a space, don't add more! */
475 } else {
476 sstvenc_ps_compute(&(cw->ps));
479 }
480 }
481}
482
483static void
485 sstvenc_ps_compute(&(cw->ps));
486 if (cw->ps.sample_idx > cw->dit_period) {
487 /* That's enough space. */
489 }
490}
491
492static void
494 sstvenc_ps_compute(&(cw->ps));
495 if (cw->ps.sample_idx > (2 * cw->dit_period)) {
496 /* That's enough space. */
498 }
499}
500
501static void sstvenc_cw_handle_state_done(struct sstvenc_cw_mod* const cw) {
502 cw->output = 0.0;
503 cw->text_string = NULL;
504 cw->symbol = NULL;
505}
506
507void sstvenc_cw_compute(struct sstvenc_cw_mod* const cw) {
508 switch (cw->state) {
512 break;
515 break;
518 break;
521 break;
523 default:
525 return;
526 }
527}
528
529size_t sstvenc_cw_fill_buffer(struct sstvenc_cw_mod* const cw, double* buffer,
530 size_t buffer_sz) {
531 size_t written_sz = 0;
532
533 while ((buffer_sz > 0) && (cw->state < SSTVENC_CW_MOD_STATE_DONE)) {
535
536 buffer[0] = cw->output;
537 buffer++;
538 buffer_sz--;
539
540 written_sz++;
541 }
542
543 return written_sz;
544}
545
#define SSTVENC_CW_MOD_STATE_DAHSPACE
Definition cw.h:85
#define SSTVENC_CW_MOD_STATE_INIT
Definition cw.h:64
#define SSTVENC_CW_MOD_STATE_DONE
Definition cw.h:91
#define SSTVENC_CW_MOD_STATE_MARK
Definition cw.h:75
#define SSTVENC_CW_MOD_STATE_DITSPACE
Definition cw.h:80
#define SSTVENC_CW_MOD_STATE_NEXT_SYM
Definition cw.h:70
struct sstvenc_oscillator osc
Definition cw.h:133
double output
Definition cw.h:114
const char * text_string
Definition cw.h:127
const char * key
Definition cw.c:19
uint8_t pos
Definition cw.h:145
struct sstvenc_pulseshape ps
Definition cw.h:136
uint16_t dit_period
Definition cw.h:139
const char * value
Definition cw.c:20
const struct sstvenc_cw_pair * symbol
Definition cw.h:130
uint8_t state
Definition cw.h:142
static struct sstvenc_cw_pair sstvenc_cw_mbsymbols[]
Definition cw.c:99
static struct sstvenc_cw_pair sstvenc_cw_symbols[]
Definition cw.c:33
static const struct sstvenc_cw_pair * sstvenc_cw_get_symbol(const char *sym)
Definition cw.c:365
static void sstvenc_cw_end_subsym(struct sstvenc_cw_mod *const cw)
Definition cw.c:432
void sstvenc_cw_compute(struct sstvenc_cw_mod *const cw)
Definition cw.c:507
static void sstvenc_cw_end_symbol(struct sstvenc_cw_mod *const cw)
Definition cw.c:446
static const struct sstvenc_cw_pair * sstvenc_cw_symbol_match(const char *sym, const struct sstvenc_cw_pair *candidate)
Definition cw.c:329
static void sstvenc_cw_handle_state_done(struct sstvenc_cw_mod *const cw)
Definition cw.c:501
static void sstvenc_cw_get_next_sym(struct sstvenc_cw_mod *const cw)
Definition cw.c:383
static void sstvenc_cw_handle_state_ditspace(struct sstvenc_cw_mod *const cw)
Definition cw.c:484
static void sstvenc_cw_handle_state_dahspace(struct sstvenc_cw_mod *const cw)
Definition cw.c:493
void sstvenc_cw_init(struct sstvenc_cw_mod *const cw, const char *text, double amplitude, double frequency, double dit_period, double slope_period, uint32_t sample_rate, uint8_t time_unit)
Definition cw.c:312
static const struct sstvenc_cw_pair * sstvenc_cw_symbol_lookup(const char *sym, const struct sstvenc_cw_pair *table, uint8_t len)
Definition cw.c:342
static void sstvenc_cw_start_mark(struct sstvenc_cw_mod *const cw)
Definition cw.c:406
static void sstvenc_cw_handle_state_mark(struct sstvenc_cw_mod *const cw)
Definition cw.c:454
size_t sstvenc_cw_fill_buffer(struct sstvenc_cw_mod *const cw, double *buffer, size_t buffer_sz)
Definition cw.c:529
void sstvenc_osc_init(struct sstvenc_oscillator *const osc, double amplitude, double frequency, double offset, uint32_t sample_rate)
Definition oscillator.c:42
void sstvenc_osc_compute(struct sstvenc_oscillator *const osc)
Definition oscillator.c:52
#define SSTVENC_PS_PHASE_HOLD
Definition pulseshape.h:58
#define SSTVENC_PS_PHASE_FALL
Definition pulseshape.h:67
#define SSTVENC_PS_PHASE_INIT
Definition pulseshape.h:35
#define SSTVENC_PS_PHASE_RISE
Definition pulseshape.h:44
#define SSTVENC_PS_PHASE_DONE
Definition pulseshape.h:75
uint32_t sample_idx
Definition pulseshape.h:97
void sstvenc_ps_compute(struct sstvenc_pulseshape *const ps)
Definition pulseshape.c:63
void sstvenc_ps_init(struct sstvenc_pulseshape *const ps, double amplitude, double rise_time, double hold_time, double fall_time, uint32_t sample_rate, uint8_t time_unit)
Definition pulseshape.c:28
void sstvenc_ps_reset(struct sstvenc_pulseshape *const ps, double hold_time, uint8_t time_unit)
Definition pulseshape.c:21
void sstvenc_ps_reset_samples(struct sstvenc_pulseshape *const ps, uint32_t hold_time)
Definition pulseshape.c:14
#define SSTVENC_TS_UNIT_SECONDS
Definition timescale.h:36
uint32_t sstvenc_ts_unit_to_samples(double time, uint32_t sample_rate, uint8_t unit)
Definition timescale.c:46