2018-10-18 15:42:43 +08:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cmath>
|
|
|
|
|
2018-11-02 20:03:28 +08:00
|
|
|
#include "common/wave.h"
|
2019-11-15 16:22:45 +08:00
|
|
|
#include "common/debug.h"
|
2018-12-22 22:09:43 +08:00
|
|
|
//#include "ft8/v1/pack.h"
|
|
|
|
//#include "ft8/v1/encode.h"
|
2019-01-03 02:54:18 +08:00
|
|
|
#include "ft8/pack.h"
|
|
|
|
#include "ft8/encode.h"
|
2018-12-22 22:09:43 +08:00
|
|
|
#include "ft8/constants.h"
|
2018-10-18 15:42:43 +08:00
|
|
|
|
2019-11-15 16:22:45 +08:00
|
|
|
#define LOG_LEVEL LOG_INFO
|
|
|
|
|
|
|
|
void gfsk_pulse(int n_spsym, float b, float *pulse) {
|
|
|
|
const float c = M_PI * sqrtf(2 / logf(2));
|
|
|
|
|
|
|
|
for (int i = 0; i < 3*n_spsym; ++i) {
|
|
|
|
float t = i/(float)n_spsym - 1.5f;
|
|
|
|
pulse[i] = (erff(c * b * (t + 0.5f)) - erff(c * b * (t - 0.5f))) / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Same as synth_fsk, but uses GFSK phase shaping
|
|
|
|
void synth_gfsk(const uint8_t *symbols, int n_sym, float f0, int n_spsym, int signal_rate, float *signal)
|
|
|
|
{
|
|
|
|
LOG(LOG_DEBUG, "n_spsym = %d\n", n_spsym);
|
|
|
|
int n_wave = n_sym * n_spsym;
|
|
|
|
float hmod = 1.0f;
|
|
|
|
|
|
|
|
// Compute the smoothed frequency waveform.
|
|
|
|
// Length = (nsym+2)*nsps samples, first and last symbols extended
|
|
|
|
float dphi_peak = 2 * M_PI * hmod / n_spsym;
|
|
|
|
float dphi[n_wave + 2*n_spsym];
|
|
|
|
|
|
|
|
// Shift frequency up by f0
|
|
|
|
for (int i = 0; i < n_wave + 2*n_spsym; ++i) {
|
|
|
|
dphi[i] = 2 * M_PI * f0 / signal_rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
float pulse[3 * n_spsym];
|
|
|
|
gfsk_pulse(n_spsym, 2.0f, pulse);
|
|
|
|
|
|
|
|
for (int i = 0; i < n_sym; ++i) {
|
|
|
|
int ib = i * n_spsym;
|
|
|
|
for (int j = 0; j < 3*n_spsym; ++j) {
|
|
|
|
dphi[j + ib] += dphi_peak*symbols[i]*pulse[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add dummy symbols at beginning and end with tone values equal to 1st and last symbol, respectively
|
|
|
|
for (int j = 0; j < 2*n_spsym; ++j) {
|
|
|
|
dphi[j] += dphi_peak*pulse[j + n_spsym]*symbols[0];
|
|
|
|
dphi[j + n_sym * n_spsym] += dphi_peak*pulse[j]*symbols[n_sym - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate and insert the audio waveform
|
|
|
|
float phi = 0;
|
|
|
|
for (int k = 0; k < n_wave; ++k) { // Don't include dummy symbols
|
|
|
|
signal[k] = sinf(phi);
|
|
|
|
phi = fmodf(phi + dphi[k + n_spsym], 2*M_PI);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply envelope shaping to the first and last symbols
|
|
|
|
int n_ramp = n_spsym / 8;
|
|
|
|
for (int i = 0; i < n_ramp; ++i) {
|
|
|
|
float env = (1 - cosf(2 * M_PI * i / (2 * n_ramp))) / 2;
|
|
|
|
signal[i] *= env;
|
|
|
|
signal[n_wave - 1 - i] *= env;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-02 15:55:54 +08:00
|
|
|
// Convert a sequence of symbols (tones) into a sinewave of continuous phase (FSK).
|
2018-11-02 20:03:28 +08:00
|
|
|
// Symbol 0 gets encoded as a sine of frequency f0, the others are spaced in increasing
|
2018-11-02 15:55:54 +08:00
|
|
|
// fashion.
|
|
|
|
void synth_fsk(const uint8_t *symbols, int num_symbols, float f0, float spacing,
|
|
|
|
float symbol_rate, float signal_rate, float *signal) {
|
2018-10-18 15:42:43 +08:00
|
|
|
float phase = 0;
|
|
|
|
float dt = 1/signal_rate;
|
|
|
|
float dt_sym = 1/symbol_rate;
|
|
|
|
float t = 0;
|
|
|
|
int j = 0;
|
|
|
|
int i = 0;
|
2018-11-02 15:55:54 +08:00
|
|
|
while (j < num_symbols) {
|
2018-10-18 15:42:43 +08:00
|
|
|
float f = f0 + symbols[j] * spacing;
|
2019-11-15 16:22:45 +08:00
|
|
|
phase = fmodf(phase + 2 * M_PI * f / signal_rate, 2 * M_PI);
|
|
|
|
signal[i] = sinf(phase);
|
2018-10-18 15:42:43 +08:00
|
|
|
t += dt;
|
|
|
|
if (t >= dt_sym) {
|
|
|
|
// Move to the next symbol
|
|
|
|
t -= dt_sym;
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-18 16:16:21 +08:00
|
|
|
void usage() {
|
|
|
|
printf("Generate a 15-second WAV file encoding a given message.\n");
|
|
|
|
printf("Usage:\n");
|
|
|
|
printf("\n");
|
2019-11-09 10:01:01 +08:00
|
|
|
printf("gen_ft8 MESSAGE WAV_FILE [FREQUENCY]\n");
|
2018-10-18 16:16:21 +08:00
|
|
|
printf("\n");
|
|
|
|
printf("(Note that you might have to enclose your message in quote marks if it contains spaces)\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-18 15:42:43 +08:00
|
|
|
int main(int argc, char **argv) {
|
2018-10-18 16:16:21 +08:00
|
|
|
// Expect two command-line arguments
|
|
|
|
if (argc < 3) {
|
|
|
|
usage();
|
|
|
|
return -1;
|
|
|
|
}
|
2018-10-18 15:42:43 +08:00
|
|
|
|
|
|
|
const char *message = argv[1];
|
|
|
|
const char *wav_path = argv[2];
|
2019-11-09 22:09:58 +08:00
|
|
|
float frequency = 1000.0;
|
2019-11-09 10:01:01 +08:00
|
|
|
if (argc > 3) {
|
2019-11-09 22:09:58 +08:00
|
|
|
frequency = atof(argv[3]);
|
2019-11-09 10:01:01 +08:00
|
|
|
}
|
2018-10-18 15:42:43 +08:00
|
|
|
|
2018-12-22 22:09:43 +08:00
|
|
|
// First, pack the text data into binary message
|
2019-01-03 02:54:18 +08:00
|
|
|
uint8_t packed[ft8::K_BYTES];
|
2018-10-29 21:28:46 +08:00
|
|
|
//int rc = packmsg(message, packed);
|
2019-01-03 02:54:18 +08:00
|
|
|
int rc = ft8::pack77(message, packed);
|
2018-10-18 15:42:43 +08:00
|
|
|
if (rc < 0) {
|
|
|
|
printf("Cannot parse message!\n");
|
|
|
|
printf("RC = %d\n", rc);
|
|
|
|
return -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Packed data: ");
|
2018-10-29 21:28:46 +08:00
|
|
|
for (int j = 0; j < 10; ++j) {
|
2018-10-18 15:42:43 +08:00
|
|
|
printf("%02x ", packed[j]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
|
2018-10-18 16:43:51 +08:00
|
|
|
// Second, encode the binary message as a sequence of FSK tones
|
2019-01-03 02:54:18 +08:00
|
|
|
uint8_t tones[ft8::NN]; // FT8_NN = 79, lack of better name at the moment
|
2018-10-29 21:28:46 +08:00
|
|
|
//genft8(packed, 0, tones);
|
2019-01-03 02:54:18 +08:00
|
|
|
ft8::genft8(packed, tones);
|
2018-10-18 15:42:43 +08:00
|
|
|
|
|
|
|
printf("FSK tones: ");
|
2019-01-03 02:54:18 +08:00
|
|
|
for (int j = 0; j < ft8::NN; ++j) {
|
2018-10-18 15:42:43 +08:00
|
|
|
printf("%d", tones[j]);
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
|
2018-10-18 16:43:51 +08:00
|
|
|
// Third, convert the FSK tones into an audio signal
|
2018-12-24 20:22:26 +08:00
|
|
|
const int sample_rate = 12000;
|
|
|
|
const float symbol_rate = 6.25f;
|
2019-01-03 02:54:18 +08:00
|
|
|
const int num_samples = (int)(0.5f + ft8::NN / symbol_rate * sample_rate);
|
2018-12-24 20:22:26 +08:00
|
|
|
const int num_silence = (15 * sample_rate - num_samples) / 2;
|
2018-10-18 15:42:43 +08:00
|
|
|
float signal[num_silence + num_samples + num_silence];
|
|
|
|
for (int i = 0; i < num_silence + num_samples + num_silence; i++) {
|
|
|
|
signal[i] = 0;
|
|
|
|
}
|
|
|
|
|
2019-11-15 16:22:45 +08:00
|
|
|
// synth_fsk(tones, ft8::NN, frequency, symbol_rate, symbol_rate, sample_rate, signal + num_silence);
|
|
|
|
synth_gfsk(tones, ft8::NN, frequency, sample_rate / symbol_rate, sample_rate, signal + num_silence);
|
2018-12-24 20:22:26 +08:00
|
|
|
save_wav(signal, num_silence + num_samples + num_silence, sample_rate, wav_path);
|
2018-10-18 15:42:43 +08:00
|
|
|
|
|
|
|
return 0;
|
2018-10-18 16:43:51 +08:00
|
|
|
}
|