mirror of
https://github.com/radio95-rnt/fm95.git
synced 2026-02-26 19:23:51 +01:00
generate the 66.5 inside the pll via the pilot
This commit is contained in:
2
.vscode/.server-controller-port.log
vendored
2
.vscode/.server-controller-port.log
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"port": 13452,
|
"port": 13452,
|
||||||
"time": 1742717063787,
|
"time": 1742742025054,
|
||||||
"version": "0.0.3"
|
"version": "0.0.3"
|
||||||
}
|
}
|
||||||
@@ -1,55 +1,59 @@
|
|||||||
#include "filters.h"
|
#include "filters.h"
|
||||||
|
|
||||||
void init_preemphasis(ResistorCapacitor *filter, float tau, float sample_rate) {
|
void init_preemphasis(ResistorCapacitor *filter, float tau, float sample_rate) {
|
||||||
filter->prev_sample = 0.0f;
|
filter->prev_sample = 0.0f;
|
||||||
filter->alpha = exp(-1 / (tau*sample_rate));
|
filter->alpha = exp(-1 / (tau*sample_rate));
|
||||||
}
|
}
|
||||||
float apply_preemphasis(ResistorCapacitor *filter, float sample) {
|
float apply_preemphasis(ResistorCapacitor *filter, float sample) {
|
||||||
float out = sample-filter->alpha*filter->prev_sample;
|
float out = sample-filter->alpha*filter->prev_sample;
|
||||||
filter->prev_sample = sample;
|
filter->prev_sample = sample;
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
float hard_clip(float sample, float threshold) {
|
float hard_clip(float sample, float threshold) {
|
||||||
return fmaxf(-threshold, fminf(threshold, sample));
|
return fmaxf(-threshold, fminf(threshold, sample));
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_pll(PLL *pll, float output_freq, float reference_freq, float loop_filter_bandwidth, int quadrature_mode, int sample_rate) {
|
void init_pll(PLL *pll, int interpolation, int decimation, float freq, float loop_filter_bandwidth, int quadrature_mode, int sample_rate) {
|
||||||
pll->phase = 0.0f;
|
pll->phase = 0.0f;
|
||||||
pll->freq = output_freq;
|
pll->freq = freq;
|
||||||
pll->ref_freq = reference_freq;
|
pll->loop_filter_state = 0.0f;
|
||||||
pll->loop_filter_state = 0.0f;
|
pll->kp = M_2PI * loop_filter_bandwidth;
|
||||||
pll->kp = 2.0f * M_PI * loop_filter_bandwidth;
|
pll->ki = 0.25f * pll->kp * pll->kp;
|
||||||
pll->ki = 0.25f * pll->kp * pll->kp;
|
pll->sample_rate = sample_rate;
|
||||||
pll->sample_rate = sample_rate;
|
pll->quadrature_mode = quadrature_mode;
|
||||||
pll->quadrature_mode = quadrature_mode;
|
pll->last_output = 0.0f;
|
||||||
|
pll->interpolation = interpolation;
|
||||||
|
pll->decimation = decimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
float apply_pll(PLL *pll, float ref_sample, float input_sample) {
|
float apply_pll(PLL *pll, float ref_sample) {
|
||||||
float phase_error;
|
float phase_error;
|
||||||
|
|
||||||
float output = sinf(pll->phase);
|
float vco_phase = pll->phase;
|
||||||
|
if(pll->quadrature_mode) vco_phase += M_PI/2.0f;
|
||||||
if (pll->quadrature_mode) {
|
|
||||||
output = sinf(pll->phase + (M_PI / 2.0f));
|
float vco_output = sinf(pll->phase);
|
||||||
}
|
if (pll->quadrature_mode) vco_output = sinf(pll->phase + (M_PI / 2.0f)); // 90 degrees
|
||||||
|
|
||||||
phase_error = ref_sample * input_sample;
|
phase_error = ref_sample * pll->last_output;
|
||||||
|
|
||||||
pll->loop_filter_state += pll->ki * phase_error / pll->sample_rate;
|
pll->loop_filter_state += pll->ki * phase_error / pll->sample_rate;
|
||||||
float loop_output = pll->loop_filter_state + pll->kp * phase_error;
|
float loop_output = pll->loop_filter_state + pll->kp * phase_error;
|
||||||
|
|
||||||
float freq_adjustment = loop_output / (2.0f * M_PI);
|
float freq_adjustment = loop_output / M_2PI;
|
||||||
float instantaneous_freq = pll->freq + freq_adjustment;
|
float instantaneous_freq = pll->freq + freq_adjustment;
|
||||||
|
|
||||||
pll->phase += 2.0f * M_PI * instantaneous_freq / pll->sample_rate;
|
pll->phase += M_2PI * instantaneous_freq / pll->sample_rate;
|
||||||
|
|
||||||
while (pll->phase >= 2.0f * M_PI) {
|
while (pll->phase >= M_2PI) {
|
||||||
pll->phase -= 2.0f * M_PI;
|
pll->phase -= M_2PI;
|
||||||
}
|
}
|
||||||
while (pll->phase < 0.0f) {
|
while (pll->phase < 0.0f) {
|
||||||
pll->phase += 2.0f * M_PI;
|
pll->phase += M_2PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
pll->last_output = sinf((vco_phase*pll->interpolation)/pll->decimation);
|
||||||
|
|
||||||
|
return pll->last_output;
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
float alpha;
|
float alpha;
|
||||||
float prev_sample;
|
float prev_sample;
|
||||||
} ResistorCapacitor;
|
} ResistorCapacitor;
|
||||||
|
|
||||||
void init_preemphasis(ResistorCapacitor *filter, float tau, float sample_rate);
|
void init_preemphasis(ResistorCapacitor *filter, float tau, float sample_rate);
|
||||||
@@ -17,14 +17,16 @@ float apply_preemphasis(ResistorCapacitor *filter, float sample);
|
|||||||
float hard_clip(float sample, float threshold);
|
float hard_clip(float sample, float threshold);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float phase;
|
float phase;
|
||||||
float freq;
|
float freq;
|
||||||
float ref_freq;
|
float loop_filter_state;
|
||||||
float loop_filter_state;
|
float kp;
|
||||||
float kp;
|
float ki;
|
||||||
float ki;
|
float last_output;
|
||||||
int sample_rate;
|
int interpolation;
|
||||||
int quadrature_mode;
|
int decimation;
|
||||||
|
int sample_rate;
|
||||||
|
int quadrature_mode;
|
||||||
} PLL;
|
} PLL;
|
||||||
void init_pll(PLL *pll, float output_freq, float refrence_freq, float loop_filter_bandwidth, int quadrature_mode, int sample_rate);
|
void init_pll(PLL *pll, int interpolation, int decimation, float freq, float loop_filter_bandwidth, int quadrature_mode, int sample_rate);
|
||||||
float apply_pll(PLL *pll, float ref_sample, float input_sample);
|
float apply_pll(PLL *pll, float ref_sample);
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
#include "fm_modulator.h"
|
#include "fm_modulator.h"
|
||||||
|
|
||||||
void init_fm_modulator(FMModulator *fm, float frequency, float deviation, float sample_rate) {
|
void init_fm_modulator(FMModulator *fm, float frequency, float deviation, float sample_rate) {
|
||||||
fm->frequency = frequency;
|
fm->frequency = frequency;
|
||||||
fm->deviation = deviation;
|
fm->deviation = deviation;
|
||||||
fm->sample_rate = sample_rate;
|
fm->sample_rate = sample_rate;
|
||||||
fm->osc_phase = 0.0f;
|
fm->osc_phase = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float modulate_fm(FMModulator *fm, float sample) {
|
float modulate_fm(FMModulator *fm, float sample) {
|
||||||
float inst_freq = fm->frequency+(sample*fm->deviation);
|
float inst_freq = fm->frequency+(sample*fm->deviation);
|
||||||
if (inst_freq < 0.0f) inst_freq = 0.0f;
|
if (inst_freq < 0.0f) inst_freq = 0.0f;
|
||||||
float out = sinf(fm->osc_phase);
|
float out = sinf(fm->osc_phase);
|
||||||
fm->osc_phase = fmodf(fm->osc_phase + ((M_2PI * inst_freq) / fm->sample_rate), M_2PI);
|
fm->osc_phase = fmodf(fm->osc_phase + ((M_2PI * inst_freq) / fm->sample_rate), M_2PI);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,10 @@
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
float frequency;
|
float frequency;
|
||||||
float deviation;
|
float deviation;
|
||||||
float osc_phase;
|
float osc_phase;
|
||||||
float sample_rate;
|
float sample_rate;
|
||||||
} FMModulator;
|
} FMModulator;
|
||||||
|
|
||||||
void init_fm_modulator(FMModulator *fm, float frequency, float deviation, float sample_rate);
|
void init_fm_modulator(FMModulator *fm, float frequency, float deviation, float sample_rate);
|
||||||
|
|||||||
@@ -1,37 +1,37 @@
|
|||||||
#include "oscillator.h"
|
#include "oscillator.h"
|
||||||
|
|
||||||
void init_oscillator(Oscillator *osc, float frequency, float sample_rate) {
|
void init_oscillator(Oscillator *osc, float frequency, float sample_rate) {
|
||||||
osc->phase = 0.0f;
|
osc->phase = 0.0f;
|
||||||
osc->phase_increment = (M_2PI * frequency) / sample_rate;
|
osc->phase_increment = (M_2PI * frequency) / sample_rate;
|
||||||
osc->sample_rate = sample_rate;
|
osc->sample_rate = sample_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void change_oscillator_frequency(Oscillator *osc, float frequency) {
|
void change_oscillator_frequency(Oscillator *osc, float frequency) {
|
||||||
osc->phase_increment = (M_2PI * frequency) / osc->sample_rate;
|
osc->phase_increment = (M_2PI * frequency) / osc->sample_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
float get_oscillator_sin_sample(Oscillator *osc) {
|
float get_oscillator_sin_sample(Oscillator *osc) {
|
||||||
float sample = sinf(osc->phase);
|
float sample = sinf(osc->phase);
|
||||||
advance_oscillator(osc);
|
advance_oscillator(osc);
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
float get_oscillator_cos_sample(Oscillator *osc) {
|
float get_oscillator_cos_sample(Oscillator *osc) {
|
||||||
float sample = cosf(osc->phase);
|
float sample = cosf(osc->phase);
|
||||||
advance_oscillator(osc);
|
advance_oscillator(osc);
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
float get_oscillator_sin_multiplier_ni(Oscillator *osc, float multiplier) { // ni = No Increment
|
float get_oscillator_sin_multiplier_ni(Oscillator *osc, float multiplier) { // ni = No Increment
|
||||||
return sinf(fmodf(osc->phase * multiplier, M_2PI));
|
return sinf(fmodf(osc->phase * multiplier, M_2PI));
|
||||||
}
|
}
|
||||||
float get_oscillator_cos_multiplier_ni(Oscillator *osc, float multiplier) {
|
float get_oscillator_cos_multiplier_ni(Oscillator *osc, float multiplier) {
|
||||||
return cosf(fmodf(osc->phase * multiplier, M_2PI));
|
return cosf(fmodf(osc->phase * multiplier, M_2PI));
|
||||||
}
|
}
|
||||||
|
|
||||||
void advance_oscillator(Oscillator *osc) {
|
void advance_oscillator(Oscillator *osc) {
|
||||||
osc->phase += osc->phase_increment;
|
osc->phase += osc->phase_increment;
|
||||||
if (osc->phase >= M_2PI) {
|
if (osc->phase >= M_2PI) {
|
||||||
osc->phase -= M_2PI;
|
osc->phase -= M_2PI;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,9 +4,9 @@
|
|||||||
#include "math.h"
|
#include "math.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float phase;
|
float phase;
|
||||||
float phase_increment;
|
float phase_increment;
|
||||||
float sample_rate;
|
float sample_rate;
|
||||||
} Oscillator;
|
} Oscillator;
|
||||||
|
|
||||||
void init_oscillator(Oscillator *osc, float frequency, float sample_rate);
|
void init_oscillator(Oscillator *osc, float frequency, float sample_rate);
|
||||||
|
|||||||
422
src/chimer95.c
422
src/chimer95.c
@@ -41,266 +41,266 @@ volatile int sequence_type = SEQ_NONE;
|
|||||||
volatile time_t last_sequence_time = 0;
|
volatile time_t last_sequence_time = 0;
|
||||||
|
|
||||||
static void stop(int signum) {
|
static void stop(int signum) {
|
||||||
(void)signum;
|
(void)signum;
|
||||||
printf("\nReceived stop signal.\n");
|
printf("\nReceived stop signal.\n");
|
||||||
to_run = 0;
|
to_run = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_version() {
|
void show_version() {
|
||||||
printf("chimer95 (GTS time signal encoder by radio95) version 1.1\n");
|
printf("chimer95 (GTS time signal encoder by radio95) version 1.1\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_help(char *name) {
|
void show_help(char *name) {
|
||||||
printf(
|
printf(
|
||||||
"Usage: %s\n"
|
"Usage: %s\n"
|
||||||
" -o,--output Override output device [default: %s]\n"
|
" -o,--output Override output device [default: %s]\n"
|
||||||
" -F,--frequency GTS Frequency [default: %.1f Hz]\n"
|
" -F,--frequency GTS Frequency [default: %.1f Hz]\n"
|
||||||
" -s,--samplerate Output Samplerate [default: %d]\n"
|
" -s,--samplerate Output Samplerate [default: %d]\n"
|
||||||
" -v,--volume Output volume [default: %.2f]\n"
|
" -v,--volume Output volume [default: %.2f]\n"
|
||||||
" -t,--offset GTS Offset [default: %d s]\n"
|
" -t,--offset GTS Offset [default: %d s]\n"
|
||||||
" -T,--test Enable test mode (plays full hour signal at end of every minute)\n"
|
" -T,--test Enable test mode (plays full hour signal at end of every minute)\n"
|
||||||
,name
|
,name
|
||||||
,OUTPUT_DEVICE
|
,OUTPUT_DEVICE
|
||||||
,DEFAULT_FREQ
|
,DEFAULT_FREQ
|
||||||
,DEFAULT_SAMPLE_RATE
|
,DEFAULT_SAMPLE_RATE
|
||||||
,DEFAULT_MASTER_VOLUME
|
,DEFAULT_MASTER_VOLUME
|
||||||
,DEFAULT_OFFSET
|
,DEFAULT_OFFSET
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_signal(float *output, int buffer_size, Oscillator *osc, float volume,
|
void generate_signal(float *output, int buffer_size, Oscillator *osc, float volume,
|
||||||
int *elapsed_samples, int total_samples, int pip_samples,
|
int *elapsed_samples, int total_samples, int pip_samples,
|
||||||
int pause_samples, int beep_samples, int num_pips) {
|
int pause_samples, int beep_samples, int num_pips) {
|
||||||
|
|
||||||
for (int i = 0; i < buffer_size; i++) {
|
for (int i = 0; i < buffer_size; i++) {
|
||||||
if (*elapsed_samples >= total_samples) {
|
if (*elapsed_samples >= total_samples) {
|
||||||
output[i] = 0;
|
output[i] = 0;
|
||||||
playing_sequence = 0;
|
playing_sequence = 0;
|
||||||
} else {
|
} else {
|
||||||
int cycle_position = *elapsed_samples;
|
int cycle_position = *elapsed_samples;
|
||||||
int pip_cycle = pip_samples + pause_samples;
|
int pip_cycle = pip_samples + pause_samples;
|
||||||
|
|
||||||
if (cycle_position < num_pips * pip_cycle) {
|
if (cycle_position < num_pips * pip_cycle) {
|
||||||
int within_cycle = cycle_position % pip_cycle;
|
int within_cycle = cycle_position % pip_cycle;
|
||||||
if (within_cycle < pip_samples) {
|
if (within_cycle < pip_samples) {
|
||||||
output[i] = get_oscillator_sin_sample(osc) * volume;
|
output[i] = get_oscillator_sin_sample(osc) * volume;
|
||||||
} else {
|
} else {
|
||||||
output[i] = 0;
|
output[i] = 0;
|
||||||
}
|
}
|
||||||
} else if (cycle_position < num_pips * pip_cycle + beep_samples) {
|
} else if (cycle_position < num_pips * pip_cycle + beep_samples) {
|
||||||
output[i] = get_oscillator_sin_sample(osc) * volume;
|
output[i] = get_oscillator_sin_sample(osc) * volume;
|
||||||
} else {
|
} else {
|
||||||
output[i] = 0;
|
output[i] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*elapsed_samples)++;
|
(*elapsed_samples)++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int check_time_for_sequence(int test_mode, int offset) {
|
int check_time_for_sequence(int test_mode, int offset) {
|
||||||
static time_t last_check = 0;
|
static time_t last_check = 0;
|
||||||
static int last_minute = -1;
|
static int last_minute = -1;
|
||||||
|
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
if (now == last_check) {
|
if (now == last_check) {
|
||||||
return SEQ_NONE;
|
return SEQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_check = now;
|
last_check = now;
|
||||||
struct tm *utc_time = gmtime(&now);
|
struct tm *utc_time = gmtime(&now);
|
||||||
int minute = utc_time->tm_min;
|
int minute = utc_time->tm_min;
|
||||||
int second = utc_time->tm_sec;
|
int second = utc_time->tm_sec;
|
||||||
|
|
||||||
if (difftime(now, last_sequence_time) < 1.0) {
|
if (difftime(now, last_sequence_time) < 1.0) {
|
||||||
return SEQ_NONE;
|
return SEQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minute == 29 && second == (56 + offset)) {
|
if (minute == 29 && second == (56 + offset)) {
|
||||||
last_sequence_time = now;
|
last_sequence_time = now;
|
||||||
return SEQ_29_56;
|
return SEQ_29_56;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minute == 59 && second == (55 + offset)) {
|
if (minute == 59 && second == (55 + offset)) {
|
||||||
last_sequence_time = now;
|
last_sequence_time = now;
|
||||||
return SEQ_59_55;
|
return SEQ_59_55;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_mode && second == (55 + offset) && minute != last_minute) {
|
if (test_mode && second == (55 + offset) && minute != last_minute) {
|
||||||
last_minute = minute;
|
last_minute = minute;
|
||||||
last_sequence_time = now;
|
last_sequence_time = now;
|
||||||
return SEQ_TEST_HOUR;
|
return SEQ_TEST_HOUR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SEQ_NONE;
|
return SEQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
show_version();
|
show_version();
|
||||||
|
|
||||||
pa_simple *output_device;
|
pa_simple *output_device;
|
||||||
char audio_output_device[64] = OUTPUT_DEVICE;
|
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
float master_volume = DEFAULT_MASTER_VOLUME;
|
||||||
float freq = DEFAULT_FREQ;
|
float freq = DEFAULT_FREQ;
|
||||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
int sample_rate = DEFAULT_SAMPLE_RATE;
|
||||||
int offset = DEFAULT_OFFSET;
|
int offset = DEFAULT_OFFSET;
|
||||||
int test_mode = 0;
|
int test_mode = 0;
|
||||||
|
|
||||||
// Parse command line arguments
|
// Parse command line arguments
|
||||||
int opt;
|
int opt;
|
||||||
const char *short_opt = "o:F:s:v:t:Th";
|
const char *short_opt = "o:F:s:v:t:Th";
|
||||||
struct option long_opt[] = {
|
struct option long_opt[] = {
|
||||||
{"output", required_argument, NULL, 'o'},
|
{"output", required_argument, NULL, 'o'},
|
||||||
{"frequency", required_argument, NULL, 'F'},
|
{"frequency", required_argument, NULL, 'F'},
|
||||||
{"samplerate", required_argument, NULL, 's'},
|
{"samplerate", required_argument, NULL, 's'},
|
||||||
{"volume", required_argument, NULL, 'v'},
|
{"volume", required_argument, NULL, 'v'},
|
||||||
{"offset", required_argument, NULL, 't'},
|
{"offset", required_argument, NULL, 't'},
|
||||||
{"test", no_argument, NULL, 'T'},
|
{"test", no_argument, NULL, 'T'},
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||||
switch(opt) {
|
switch(opt) {
|
||||||
case 'o':
|
case 'o':
|
||||||
strncpy(audio_output_device, optarg, sizeof(audio_output_device) - 1);
|
strncpy(audio_output_device, optarg, sizeof(audio_output_device) - 1);
|
||||||
audio_output_device[sizeof(audio_output_device) - 1] = '\0';
|
audio_output_device[sizeof(audio_output_device) - 1] = '\0';
|
||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
freq = strtof(optarg, NULL);
|
freq = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
sample_rate = strtol(optarg, NULL, 10);
|
sample_rate = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
master_volume = strtof(optarg, NULL);
|
master_volume = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
offset = strtol(optarg, NULL, 10);
|
offset = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
test_mode = 1;
|
test_mode = 1;
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
show_help(argv[0]);
|
show_help(argv[0]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Configuration:\n");
|
printf("Configuration:\n");
|
||||||
printf(" Output device: %s\n", audio_output_device);
|
printf(" Output device: %s\n", audio_output_device);
|
||||||
printf(" Frequency: %.1f Hz\n", freq);
|
printf(" Frequency: %.1f Hz\n", freq);
|
||||||
printf(" Sample rate: %d Hz\n", sample_rate);
|
printf(" Sample rate: %d Hz\n", sample_rate);
|
||||||
printf(" Volume: %.2f\n", master_volume);
|
printf(" Volume: %.2f\n", master_volume);
|
||||||
printf(" Time offset: %d seconds\n", offset);
|
printf(" Time offset: %d seconds\n", offset);
|
||||||
printf(" Test mode: %s\n", test_mode ? "Enabled" : "Disabled");
|
printf(" Test mode: %s\n", test_mode ? "Enabled" : "Disabled");
|
||||||
|
|
||||||
// Setup PulseAudio
|
// Setup PulseAudio
|
||||||
pa_sample_spec mono_format = {
|
pa_sample_spec mono_format = {
|
||||||
.format = PA_SAMPLE_FLOAT32NE,
|
.format = PA_SAMPLE_FLOAT32NE,
|
||||||
.channels = 1,
|
.channels = 1,
|
||||||
.rate = sample_rate
|
.rate = sample_rate
|
||||||
};
|
};
|
||||||
|
|
||||||
pa_buffer_attr output_buffer_atr = {
|
pa_buffer_attr output_buffer_atr = {
|
||||||
.maxlength = buffer_maxlength,
|
.maxlength = buffer_maxlength,
|
||||||
.tlength = buffer_tlength_fragsize,
|
.tlength = buffer_tlength_fragsize,
|
||||||
.prebuf = buffer_prebuf
|
.prebuf = buffer_prebuf
|
||||||
};
|
};
|
||||||
|
|
||||||
int pulse_error;
|
int pulse_error;
|
||||||
|
|
||||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
printf("Connecting to output device... (%s)\n", audio_output_device);
|
||||||
|
|
||||||
output_device = pa_simple_new(
|
output_device = pa_simple_new(
|
||||||
NULL,
|
NULL,
|
||||||
"chimer95",
|
"chimer95",
|
||||||
PA_STREAM_PLAYBACK,
|
PA_STREAM_PLAYBACK,
|
||||||
audio_output_device,
|
audio_output_device,
|
||||||
"GTS Output",
|
"GTS Output",
|
||||||
&mono_format,
|
&mono_format,
|
||||||
NULL,
|
NULL,
|
||||||
&output_buffer_atr,
|
&output_buffer_atr,
|
||||||
&pulse_error
|
&pulse_error
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!output_device) {
|
if (!output_device) {
|
||||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(pulse_error));
|
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(pulse_error));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Oscillator osc;
|
Oscillator osc;
|
||||||
init_oscillator(&osc, freq, sample_rate);
|
init_oscillator(&osc, freq, sample_rate);
|
||||||
|
|
||||||
signal(SIGINT, stop);
|
signal(SIGINT, stop);
|
||||||
signal(SIGTERM, stop);
|
signal(SIGTERM, stop);
|
||||||
|
|
||||||
float output[BUFFER_SIZE];
|
float output[BUFFER_SIZE];
|
||||||
|
|
||||||
int pip_samples = (int)((PIP_DURATION / 1000.0) * sample_rate);
|
int pip_samples = (int)((PIP_DURATION / 1000.0) * sample_rate);
|
||||||
int pause_samples = (int)((PIP_PAUSE / 1000.0) * sample_rate);
|
int pause_samples = (int)((PIP_PAUSE / 1000.0) * sample_rate);
|
||||||
int beep_samples = (int)((BEEP_DURATION / 1000.0) * sample_rate);
|
int beep_samples = (int)((BEEP_DURATION / 1000.0) * sample_rate);
|
||||||
|
|
||||||
int samples_29_56 = 4 * (pip_samples + pause_samples) + beep_samples;
|
int samples_29_56 = 4 * (pip_samples + pause_samples) + beep_samples;
|
||||||
int samples_59_55 = 5 * (pip_samples + pause_samples) + beep_samples;
|
int samples_59_55 = 5 * (pip_samples + pause_samples) + beep_samples;
|
||||||
|
|
||||||
printf("Ready to play time signals.\n");
|
printf("Ready to play time signals.\n");
|
||||||
printf("Will trigger at XX:29:%02d and XX:59:%02d\n", 56+offset, 55+offset);
|
printf("Will trigger at XX:29:%02d and XX:59:%02d\n", 56+offset, 55+offset);
|
||||||
if (test_mode) {
|
if (test_mode) {
|
||||||
printf("TEST MODE: Will also play full hour signal at the end of every minute\n");
|
printf("TEST MODE: Will also play full hour signal at the end of every minute\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int elapsed_samples = 0;
|
int elapsed_samples = 0;
|
||||||
int total_sequence_samples = 0;
|
int total_sequence_samples = 0;
|
||||||
int sequence_completed = 0;
|
int sequence_completed = 0;
|
||||||
|
|
||||||
while (to_run) {
|
while (to_run) {
|
||||||
if (!playing_sequence) {
|
if (!playing_sequence) {
|
||||||
int new_sequence = check_time_for_sequence(test_mode, offset);
|
int new_sequence = check_time_for_sequence(test_mode, offset);
|
||||||
|
|
||||||
if (new_sequence != SEQ_NONE) {
|
if (new_sequence != SEQ_NONE) {
|
||||||
playing_sequence = 1;
|
playing_sequence = 1;
|
||||||
sequence_type = new_sequence;
|
sequence_type = new_sequence;
|
||||||
elapsed_samples = 0;
|
elapsed_samples = 0;
|
||||||
sequence_completed = 0;
|
sequence_completed = 0;
|
||||||
|
|
||||||
if (new_sequence == SEQ_29_56) {
|
if (new_sequence == SEQ_29_56) {
|
||||||
total_sequence_samples = samples_29_56;
|
total_sequence_samples = samples_29_56;
|
||||||
} else {
|
} else {
|
||||||
total_sequence_samples = samples_59_55;
|
total_sequence_samples = samples_59_55;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(output, 0, sizeof(output));
|
memset(output, 0, sizeof(output));
|
||||||
} else {
|
} else {
|
||||||
static int idle_counter = 0;
|
static int idle_counter = 0;
|
||||||
if (idle_counter++ % 10 == 0) {
|
if (idle_counter++ % 10 == 0) {
|
||||||
memset(output, 0, sizeof(output));
|
memset(output, 0, sizeof(output));
|
||||||
pa_simple_write(output_device, output, sizeof(output), &pulse_error);
|
pa_simple_write(output_device, output, sizeof(output), &pulse_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec ts = {0, 5000000}; // 5ms sleep
|
struct timespec ts = {0, 5000000}; // 5ms sleep
|
||||||
nanosleep(&ts, NULL);
|
nanosleep(&ts, NULL);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_pips = (sequence_type == SEQ_29_56) ? 4 : 5;
|
int num_pips = (sequence_type == SEQ_29_56) ? 4 : 5;
|
||||||
generate_signal(output, BUFFER_SIZE, &osc, master_volume,
|
generate_signal(output, BUFFER_SIZE, &osc, master_volume,
|
||||||
&elapsed_samples, total_sequence_samples,
|
&elapsed_samples, total_sequence_samples,
|
||||||
pip_samples, pause_samples, beep_samples, num_pips);
|
pip_samples, pause_samples, beep_samples, num_pips);
|
||||||
|
|
||||||
if (!playing_sequence && !sequence_completed) {
|
if (!playing_sequence && !sequence_completed) {
|
||||||
sequence_completed = 1;
|
sequence_completed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
||||||
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
||||||
to_run = 0;
|
to_run = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Cleaning up...\n");
|
printf("Cleaning up...\n");
|
||||||
pa_simple_free(output_device);
|
pa_simple_free(output_device);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
674
src/dcf95.c
674
src/dcf95.c
@@ -49,437 +49,437 @@ volatile int dcf77_bits[60];
|
|||||||
unsigned int lfsr = 0;
|
unsigned int lfsr = 0;
|
||||||
|
|
||||||
static void stop(int signum) {
|
static void stop(int signum) {
|
||||||
(void)signum;
|
(void)signum;
|
||||||
printf("\nReceived stop signal.\n");
|
printf("\nReceived stop signal.\n");
|
||||||
to_run = 0;
|
to_run = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int generate_chip() {
|
unsigned int generate_chip() {
|
||||||
unsigned int chip = lfsr & 1;
|
unsigned int chip = lfsr & 1;
|
||||||
|
|
||||||
lfsr >>= 1;
|
lfsr >>= 1;
|
||||||
if (chip || !lfsr)
|
if (chip || !lfsr)
|
||||||
lfsr ^= 0x110;
|
lfsr ^= 0x110;
|
||||||
|
|
||||||
return chip;
|
return chip;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_lfsr() {
|
void reset_lfsr() {
|
||||||
lfsr = 0;
|
lfsr = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int is_cet_dst(struct tm *tm_time) {
|
int is_cet_dst(struct tm *tm_time) {
|
||||||
int month = tm_time->tm_mon + 1;
|
int month = tm_time->tm_mon + 1;
|
||||||
int day = tm_time->tm_mday;
|
int day = tm_time->tm_mday;
|
||||||
int hour = tm_time->tm_hour;
|
int hour = tm_time->tm_hour;
|
||||||
|
|
||||||
if (month == 3) {
|
if (month == 3) {
|
||||||
int last_sunday = 31 - ((5 + 31) % 7);
|
int last_sunday = 31 - ((5 + 31) % 7);
|
||||||
if ((day > last_sunday) || (day == last_sunday && hour >= 2)) {
|
if ((day > last_sunday) || (day == last_sunday && hour >= 2)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else if (month > 3 && month < 10) {
|
} else if (month > 3 && month < 10) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (month == 10) {
|
} else if (month == 10) {
|
||||||
int last_sunday = 31 - ((5 + 31) % 7);
|
int last_sunday = 31 - ((5 + 31) % 7);
|
||||||
if ((day < last_sunday) || (day == last_sunday && hour < 3)) {
|
if ((day < last_sunday) || (day == last_sunday && hour < 3)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int is_timezone_change_soon() {
|
int is_timezone_change_soon() {
|
||||||
time_t now, in_an_hour;
|
time_t now, in_an_hour;
|
||||||
struct tm cet_now, cet_later;
|
struct tm cet_now, cet_later;
|
||||||
|
|
||||||
time(&now);
|
time(&now);
|
||||||
in_an_hour = now + 3600;
|
in_an_hour = now + 3600;
|
||||||
|
|
||||||
memset(&cet_now, 0, sizeof(struct tm));
|
memset(&cet_now, 0, sizeof(struct tm));
|
||||||
memset(&cet_later, 0, sizeof(struct tm));
|
memset(&cet_later, 0, sizeof(struct tm));
|
||||||
|
|
||||||
struct tm *gm_now = gmtime(&now);
|
struct tm *gm_now = gmtime(&now);
|
||||||
struct tm *gm_later = gmtime(&in_an_hour);
|
struct tm *gm_later = gmtime(&in_an_hour);
|
||||||
|
|
||||||
cet_now = *gm_now;
|
cet_now = *gm_now;
|
||||||
cet_later = *gm_later;
|
cet_later = *gm_later;
|
||||||
|
|
||||||
cet_now.tm_hour += 1;
|
cet_now.tm_hour += 1;
|
||||||
cet_later.tm_hour += 1;
|
cet_later.tm_hour += 1;
|
||||||
|
|
||||||
int is_dst_now = is_cet_dst(&cet_now);
|
int is_dst_now = is_cet_dst(&cet_now);
|
||||||
int is_dst_later = is_cet_dst(&cet_later);
|
int is_dst_later = is_cet_dst(&cet_later);
|
||||||
|
|
||||||
if (is_dst_now) cet_now.tm_hour += 1;
|
if (is_dst_now) cet_now.tm_hour += 1;
|
||||||
if (is_dst_later) cet_later.tm_hour += 1;
|
if (is_dst_later) cet_later.tm_hour += 1;
|
||||||
|
|
||||||
mktime(&cet_now);
|
mktime(&cet_now);
|
||||||
mktime(&cet_later);
|
mktime(&cet_later);
|
||||||
|
|
||||||
return is_dst_now != is_dst_later;
|
return is_dst_now != is_dst_later;
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculate_dcf77_bits(time_t now, int *bits) {
|
void calculate_dcf77_bits(time_t now, int *bits) {
|
||||||
struct tm *t = gmtime(&now);
|
struct tm *t = gmtime(&now);
|
||||||
int cest = is_cet_dst(t);
|
int cest = is_cet_dst(t);
|
||||||
|
|
||||||
memset(bits, 0, 60 * sizeof(int));
|
memset(bits, 0, 60 * sizeof(int));
|
||||||
|
|
||||||
bits[16] = is_timezone_change_soon();
|
bits[16] = is_timezone_change_soon();
|
||||||
if(cest) {
|
if(cest) {
|
||||||
bits[17] = 1;
|
bits[17] = 1;
|
||||||
} else {
|
} else {
|
||||||
bits[18] = 1;
|
bits[18] = 1;
|
||||||
}
|
}
|
||||||
bits[20] = 1;
|
bits[20] = 1;
|
||||||
|
|
||||||
int minutes = t->tm_min;
|
int minutes = t->tm_min;
|
||||||
bits[21] = (minutes % 10) & 0x01;
|
bits[21] = (minutes % 10) & 0x01;
|
||||||
bits[22] = ((minutes % 10) >> 1) & 0x01;
|
bits[22] = ((minutes % 10) >> 1) & 0x01;
|
||||||
bits[23] = ((minutes % 10) >> 2) & 0x01;
|
bits[23] = ((minutes % 10) >> 2) & 0x01;
|
||||||
bits[24] = ((minutes % 10) >> 3) & 0x01;
|
bits[24] = ((minutes % 10) >> 3) & 0x01;
|
||||||
bits[25] = ((minutes / 10) & 0x01);
|
bits[25] = ((minutes / 10) & 0x01);
|
||||||
bits[26] = ((minutes / 10) >> 1) & 0x01;
|
bits[26] = ((minutes / 10) >> 1) & 0x01;
|
||||||
bits[27] = ((minutes / 10) >> 2) & 0x01;
|
bits[27] = ((minutes / 10) >> 2) & 0x01;
|
||||||
|
|
||||||
int parity = 0;
|
int parity = 0;
|
||||||
for (int i = 21; i <= 27; i++) {
|
for (int i = 21; i <= 27; i++) {
|
||||||
parity ^= bits[i];
|
parity ^= bits[i];
|
||||||
}
|
}
|
||||||
bits[28] = parity;
|
bits[28] = parity;
|
||||||
|
|
||||||
int hours = t->tm_hour-1;
|
int hours = t->tm_hour-1;
|
||||||
hours += 1;
|
hours += 1;
|
||||||
if(cest) hours += 1;
|
if(cest) hours += 1;
|
||||||
bits[29] = (hours % 10) & 0x01;
|
bits[29] = (hours % 10) & 0x01;
|
||||||
bits[30] = ((hours % 10) >> 1) & 0x01;
|
bits[30] = ((hours % 10) >> 1) & 0x01;
|
||||||
bits[31] = ((hours % 10) >> 2) & 0x01;
|
bits[31] = ((hours % 10) >> 2) & 0x01;
|
||||||
bits[32] = ((hours % 10) >> 3) & 0x01;
|
bits[32] = ((hours % 10) >> 3) & 0x01;
|
||||||
bits[33] = ((hours / 10) & 0x01);
|
bits[33] = ((hours / 10) & 0x01);
|
||||||
bits[34] = ((hours / 10) >> 1) & 0x01;
|
bits[34] = ((hours / 10) >> 1) & 0x01;
|
||||||
|
|
||||||
parity = 0;
|
parity = 0;
|
||||||
for (int i = 29; i <= 34; i++) {
|
for (int i = 29; i <= 34; i++) {
|
||||||
parity ^= bits[i];
|
parity ^= bits[i];
|
||||||
}
|
}
|
||||||
bits[35] = parity;
|
bits[35] = parity;
|
||||||
|
|
||||||
int day = t->tm_mday;
|
int day = t->tm_mday;
|
||||||
bits[36] = (day % 10) & 0x01;
|
bits[36] = (day % 10) & 0x01;
|
||||||
bits[37] = ((day % 10) >> 1) & 0x01;
|
bits[37] = ((day % 10) >> 1) & 0x01;
|
||||||
bits[38] = ((day % 10) >> 2) & 0x01;
|
bits[38] = ((day % 10) >> 2) & 0x01;
|
||||||
bits[39] = ((day % 10) >> 3) & 0x01;
|
bits[39] = ((day % 10) >> 3) & 0x01;
|
||||||
bits[40] = ((day / 10) & 0x01);
|
bits[40] = ((day / 10) & 0x01);
|
||||||
bits[41] = ((day / 10) >> 1) & 0x01;
|
bits[41] = ((day / 10) >> 1) & 0x01;
|
||||||
|
|
||||||
int dow = t->tm_wday == 0 ? 7 : t->tm_wday;
|
int dow = t->tm_wday == 0 ? 7 : t->tm_wday;
|
||||||
bits[42] = dow & 0x01;
|
bits[42] = dow & 0x01;
|
||||||
bits[43] = (dow >> 1) & 0x01;
|
bits[43] = (dow >> 1) & 0x01;
|
||||||
bits[44] = (dow >> 2) & 0x01;
|
bits[44] = (dow >> 2) & 0x01;
|
||||||
|
|
||||||
int month = t->tm_mon + 1;
|
int month = t->tm_mon + 1;
|
||||||
bits[45] = (month % 10) & 0x01;
|
bits[45] = (month % 10) & 0x01;
|
||||||
bits[46] = ((month % 10) >> 1) & 0x01;
|
bits[46] = ((month % 10) >> 1) & 0x01;
|
||||||
bits[47] = ((month % 10) >> 2) & 0x01;
|
bits[47] = ((month % 10) >> 2) & 0x01;
|
||||||
bits[48] = ((month % 10) >> 3) & 0x01;
|
bits[48] = ((month % 10) >> 3) & 0x01;
|
||||||
bits[49] = (month / 10) & 0x01;
|
bits[49] = (month / 10) & 0x01;
|
||||||
|
|
||||||
int year = t->tm_year % 100;
|
int year = t->tm_year % 100;
|
||||||
bits[50] = (year % 10) & 0x01;
|
bits[50] = (year % 10) & 0x01;
|
||||||
bits[51] = ((year % 10) >> 1) & 0x01;
|
bits[51] = ((year % 10) >> 1) & 0x01;
|
||||||
bits[52] = ((year % 10) >> 2) & 0x01;
|
bits[52] = ((year % 10) >> 2) & 0x01;
|
||||||
bits[53] = ((year % 10) >> 3) & 0x01;
|
bits[53] = ((year % 10) >> 3) & 0x01;
|
||||||
bits[54] = ((year / 10) & 0x01);
|
bits[54] = ((year / 10) & 0x01);
|
||||||
bits[55] = ((year / 10) >> 1) & 0x01;
|
bits[55] = ((year / 10) >> 1) & 0x01;
|
||||||
bits[56] = ((year / 10) >> 2) & 0x01;
|
bits[56] = ((year / 10) >> 2) & 0x01;
|
||||||
bits[57] = ((year / 10) >> 3) & 0x01;
|
bits[57] = ((year / 10) >> 3) & 0x01;
|
||||||
|
|
||||||
parity = 0;
|
parity = 0;
|
||||||
for (int i = 36; i <= 57; i++) {
|
for (int i = 36; i <= 57; i++) {
|
||||||
parity ^= bits[i];
|
parity ^= bits[i];
|
||||||
}
|
}
|
||||||
bits[58] = parity;
|
bits[58] = parity;
|
||||||
|
|
||||||
bits[59] = 2;
|
bits[59] = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_dcf77_bits(const int *bits) {
|
void print_dcf77_bits(const int *bits) {
|
||||||
printf("DCF77 Bit Pattern: ");
|
printf("DCF77 Bit Pattern: ");
|
||||||
for (int i = 0; i < 60; i++) {
|
for (int i = 0; i < 60; i++) {
|
||||||
printf("%d", bits[i]);
|
printf("%d", bits[i]);
|
||||||
if ((i+1) % 10 == 0) printf(" ");
|
if ((i+1) % 10 == 0) printf(" ");
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_version() {
|
void show_version() {
|
||||||
printf("dcf95 (DCF77 time signal encoder by radio95) version 1.1\n");
|
printf("dcf95 (DCF77 time signal encoder by radio95) version 1.1\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_help(char *name) {
|
void show_help(char *name) {
|
||||||
printf(
|
printf(
|
||||||
"Usage: %s\n"
|
"Usage: %s\n"
|
||||||
" -o,--output Override output device [default: %s]\n"
|
" -o,--output Override output device [default: %s]\n"
|
||||||
" -F,--frequency DCF77 Frequency [default: %.1f Hz]\n"
|
" -F,--frequency DCF77 Frequency [default: %.1f Hz]\n"
|
||||||
" -s,--samplerate Output Samplerate [default: %d]\n"
|
" -s,--samplerate Output Samplerate [default: %d]\n"
|
||||||
" -v,--volume Output volume [default: %.2f]\n"
|
" -v,--volume Output volume [default: %.2f]\n"
|
||||||
" -t,--offset Time Offset [default: %ds]\n"
|
" -t,--offset Time Offset [default: %ds]\n"
|
||||||
" -T,--test Enable test mode\n"
|
" -T,--test Enable test mode\n"
|
||||||
" -n,--no-phase Disable phase modulation\n"
|
" -n,--no-phase Disable phase modulation\n"
|
||||||
,name
|
,name
|
||||||
,OUTPUT_DEVICE
|
,OUTPUT_DEVICE
|
||||||
,DEFAULT_FREQ
|
,DEFAULT_FREQ
|
||||||
,DEFAULT_SAMPLE_RATE
|
,DEFAULT_SAMPLE_RATE
|
||||||
,DEFAULT_MASTER_VOLUME
|
,DEFAULT_MASTER_VOLUME
|
||||||
,DEFAULT_OFFSET
|
,DEFAULT_OFFSET
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
show_version();
|
show_version();
|
||||||
|
|
||||||
pa_simple *output_device;
|
pa_simple *output_device;
|
||||||
|
|
||||||
char audio_output_device[64] = OUTPUT_DEVICE;
|
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||||
|
|
||||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
float master_volume = DEFAULT_MASTER_VOLUME;
|
||||||
float freq = DEFAULT_FREQ;
|
float freq = DEFAULT_FREQ;
|
||||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
int sample_rate = DEFAULT_SAMPLE_RATE;
|
||||||
int offset = DEFAULT_OFFSET;
|
int offset = DEFAULT_OFFSET;
|
||||||
int test_mode = 0;
|
int test_mode = 0;
|
||||||
int no_phase = 0;
|
int no_phase = 0;
|
||||||
|
|
||||||
// #region Parse Arguments
|
// #region Parse Arguments
|
||||||
int opt;
|
int opt;
|
||||||
const char *short_opt = "o:F:s:v:t:Tnh";
|
const char *short_opt = "o:F:s:v:t:Tnh";
|
||||||
struct option long_opt[] =
|
struct option long_opt[] =
|
||||||
{
|
{
|
||||||
{"output", required_argument, NULL, 'o'},
|
{"output", required_argument, NULL, 'o'},
|
||||||
{"frequency", required_argument, NULL, 'F'},
|
{"frequency", required_argument, NULL, 'F'},
|
||||||
{"samplerate", required_argument, NULL, 's'},
|
{"samplerate", required_argument, NULL, 's'},
|
||||||
{"volume", required_argument, NULL, 'v'},
|
{"volume", required_argument, NULL, 'v'},
|
||||||
{"offset", required_argument, NULL, 't'},
|
{"offset", required_argument, NULL, 't'},
|
||||||
{"test", no_argument, NULL, 'T'},
|
{"test", no_argument, NULL, 'T'},
|
||||||
{"no-phase", no_argument, NULL, 'n'},
|
{"no-phase", no_argument, NULL, 'n'},
|
||||||
|
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||||
switch(opt) {
|
switch(opt) {
|
||||||
case 'o': // Output Device
|
case 'o': // Output Device
|
||||||
memcpy(audio_output_device, optarg, 63);
|
memcpy(audio_output_device, optarg, 63);
|
||||||
audio_output_device[63] = '\0'; // Ensure null-termination
|
audio_output_device[63] = '\0'; // Ensure null-termination
|
||||||
break;
|
break;
|
||||||
case 'F': // Frequency
|
case 'F': // Frequency
|
||||||
freq = strtof(optarg, NULL);
|
freq = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 's': // Sample rate
|
case 's': // Sample rate
|
||||||
sample_rate = strtol(optarg, NULL, 10);
|
sample_rate = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'v': // Volume
|
case 'v': // Volume
|
||||||
master_volume = strtof(optarg, NULL);
|
master_volume = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 't': // Offset
|
case 't': // Offset
|
||||||
offset = strtol(optarg, NULL, 10);
|
offset = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'T': // Test mode
|
case 'T': // Test mode
|
||||||
test_mode = 1;
|
test_mode = 1;
|
||||||
break;
|
break;
|
||||||
case 'n': // Disable phase modulation
|
case 'n': // Disable phase modulation
|
||||||
no_phase = 1;
|
no_phase = 1;
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
show_help(argv[0]);
|
show_help(argv[0]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
if(test_mode) {
|
if(test_mode) {
|
||||||
time_t now = time(NULL) + offset + 60;
|
time_t now = time(NULL) + offset + 60;
|
||||||
calculate_dcf77_bits(now, (int *)dcf77_bits);
|
calculate_dcf77_bits(now, (int *)dcf77_bits);
|
||||||
print_dcf77_bits((int *)dcf77_bits);
|
print_dcf77_bits((int *)dcf77_bits);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Configuration:\n");
|
printf("Configuration:\n");
|
||||||
printf(" Output device: %s\n", audio_output_device);
|
printf(" Output device: %s\n", audio_output_device);
|
||||||
printf(" Frequency: %.1f Hz\n", freq);
|
printf(" Frequency: %.1f Hz\n", freq);
|
||||||
printf(" Sample rate: %d Hz\n", sample_rate);
|
printf(" Sample rate: %d Hz\n", sample_rate);
|
||||||
printf(" Volume: %.2f\n", master_volume);
|
printf(" Volume: %.2f\n", master_volume);
|
||||||
printf(" Time offset: %d seconds\n", offset);
|
printf(" Time offset: %d seconds\n", offset);
|
||||||
if (no_phase) {
|
if (no_phase) {
|
||||||
printf(" Phase modulation: Disabled\n");
|
printf(" Phase modulation: Disabled\n");
|
||||||
} else {
|
} else {
|
||||||
printf(" Phase modulation: +/- %.1f degrees\n", PHASE_SHIFT);
|
printf(" Phase modulation: +/- %.1f degrees\n", PHASE_SHIFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region Setup devices
|
// #region Setup devices
|
||||||
pa_sample_spec mono_format = {
|
pa_sample_spec mono_format = {
|
||||||
.format = PA_SAMPLE_FLOAT32NE,
|
.format = PA_SAMPLE_FLOAT32NE,
|
||||||
.channels = 1,
|
.channels = 1,
|
||||||
.rate = sample_rate
|
.rate = sample_rate
|
||||||
};
|
};
|
||||||
|
|
||||||
pa_buffer_attr output_buffer_atr = {
|
pa_buffer_attr output_buffer_atr = {
|
||||||
.maxlength = buffer_maxlength,
|
.maxlength = buffer_maxlength,
|
||||||
.tlength = buffer_tlength_fragsize,
|
.tlength = buffer_tlength_fragsize,
|
||||||
.prebuf = buffer_prebuf
|
.prebuf = buffer_prebuf
|
||||||
};
|
};
|
||||||
|
|
||||||
int opentime_pulse_error;
|
int opentime_pulse_error;
|
||||||
|
|
||||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
printf("Connecting to output device... (%s)\n", audio_output_device);
|
||||||
|
|
||||||
output_device = pa_simple_new(
|
output_device = pa_simple_new(
|
||||||
NULL,
|
NULL,
|
||||||
"dcf95",
|
"dcf95",
|
||||||
PA_STREAM_PLAYBACK,
|
PA_STREAM_PLAYBACK,
|
||||||
audio_output_device,
|
audio_output_device,
|
||||||
"DCF77 Output",
|
"DCF77 Output",
|
||||||
&mono_format,
|
&mono_format,
|
||||||
NULL,
|
NULL,
|
||||||
&output_buffer_atr,
|
&output_buffer_atr,
|
||||||
&opentime_pulse_error
|
&opentime_pulse_error
|
||||||
);
|
);
|
||||||
if (!output_device) {
|
if (!output_device) {
|
||||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(opentime_pulse_error));
|
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(opentime_pulse_error));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
Oscillator osc;
|
Oscillator osc;
|
||||||
init_oscillator(&osc, freq, sample_rate);
|
init_oscillator(&osc, freq, sample_rate);
|
||||||
|
|
||||||
signal(SIGINT, stop);
|
signal(SIGINT, stop);
|
||||||
signal(SIGTERM, stop);
|
signal(SIGTERM, stop);
|
||||||
|
|
||||||
int pulse_error;
|
int pulse_error;
|
||||||
float output[BUFFER_SIZE];
|
float output[BUFFER_SIZE];
|
||||||
|
|
||||||
int current_second = -1;
|
int current_second = -1;
|
||||||
int ms_within_second = 0;
|
int ms_within_second = 0;
|
||||||
|
|
||||||
int dsss_start_samples = (int)((DSSS_START_MS / 1000.0) * sample_rate);
|
int dsss_start_samples = (int)((DSSS_START_MS / 1000.0) * sample_rate);
|
||||||
int dsss_duration_samples = (int)((DSSS_DURATION_MS / 1000.0) * sample_rate);
|
int dsss_duration_samples = (int)((DSSS_DURATION_MS / 1000.0) * sample_rate);
|
||||||
int dsss_end_samples = dsss_start_samples + dsss_duration_samples;
|
int dsss_end_samples = dsss_start_samples + dsss_duration_samples;
|
||||||
float phase_shift_rad = (PHASE_SHIFT * M_PI) / 180.0;
|
float phase_shift_rad = (PHASE_SHIFT * M_PI) / 180.0;
|
||||||
|
|
||||||
int current_chip_count = 0;
|
int current_chip_count = 0;
|
||||||
int current_cycle_count = 0;
|
int current_cycle_count = 0;
|
||||||
int in_dsss_period = 0;
|
int in_dsss_period = 0;
|
||||||
|
|
||||||
int elapsed_samples = 0;
|
int elapsed_samples = 0;
|
||||||
|
|
||||||
printf("DCF77 encoder ready.\n");
|
printf("DCF77 encoder ready.\n");
|
||||||
|
|
||||||
while (to_run) {
|
while (to_run) {
|
||||||
memset(output, 0, sizeof(output));
|
memset(output, 0, sizeof(output));
|
||||||
|
|
||||||
time_t now = time(NULL) + offset + 60;
|
time_t now = time(NULL) + offset + 60;
|
||||||
struct tm *t = gmtime(&now);
|
struct tm *t = gmtime(&now);
|
||||||
int second = t->tm_sec;
|
int second = t->tm_sec;
|
||||||
|
|
||||||
if (second == 0 && current_second != 0) {
|
if (second == 0 && current_second != 0) {
|
||||||
calculate_dcf77_bits(now, (int *)dcf77_bits);
|
calculate_dcf77_bits(now, (int *)dcf77_bits);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
print_dcf77_bits((int *)dcf77_bits);
|
print_dcf77_bits((int *)dcf77_bits);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bit_position = 0;
|
bit_position = 0;
|
||||||
elapsed_samples = 0;
|
elapsed_samples = 0;
|
||||||
transmitting = 1;
|
transmitting = 1;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("Starting new DCF77 transmission for %02d:%02d:%02d UTC\n",
|
printf("Starting new DCF77 transmission for %02d:%02d:%02d UTC\n",
|
||||||
t->tm_hour, t->tm_min, t->tm_sec);
|
t->tm_hour, t->tm_min, t->tm_sec);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (second != current_second) {
|
if (second != current_second) {
|
||||||
current_second = second;
|
current_second = second;
|
||||||
|
|
||||||
reset_lfsr();
|
reset_lfsr();
|
||||||
current_chip_count = 0;
|
current_chip_count = 0;
|
||||||
current_cycle_count = 0;
|
current_cycle_count = 0;
|
||||||
|
|
||||||
if (transmitting) {
|
if (transmitting) {
|
||||||
if (bit_position < 59) {
|
if (bit_position < 59) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("Bit %2d: %d\n", bit_position, dcf77_bits[bit_position]);
|
printf("Bit %2d: %d\n", bit_position, dcf77_bits[bit_position]);
|
||||||
#endif
|
#endif
|
||||||
bit_position++;
|
bit_position++;
|
||||||
} else {
|
} else {
|
||||||
bit_position = 0;
|
bit_position = 0;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf("End of minute, restarting bit sequence.\n");
|
printf("End of minute, restarting bit sequence.\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsed_samples = 0;
|
elapsed_samples = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||||
ms_within_second = (int)((elapsed_samples * 1000.0) / sample_rate);
|
ms_within_second = (int)((elapsed_samples * 1000.0) / sample_rate);
|
||||||
|
|
||||||
int current_bit = bit_position > 0 ? bit_position - 1 : 59;
|
int current_bit = bit_position > 0 ? bit_position - 1 : 59;
|
||||||
|
|
||||||
in_dsss_period = (elapsed_samples >= dsss_start_samples &&
|
in_dsss_period = (elapsed_samples >= dsss_start_samples &&
|
||||||
elapsed_samples < dsss_end_samples);
|
elapsed_samples < dsss_end_samples);
|
||||||
|
|
||||||
float phase_offset = 0.0;
|
float phase_offset = 0.0;
|
||||||
|
|
||||||
if (in_dsss_period && transmitting && !no_phase) {
|
if (in_dsss_period && transmitting && !no_phase) {
|
||||||
if (current_cycle_count == 0) {
|
if (current_cycle_count == 0) {
|
||||||
if (current_chip_count < CHIPS_PER_BIT) {
|
if (current_chip_count < CHIPS_PER_BIT) {
|
||||||
unsigned int chip = generate_chip();
|
unsigned int chip = generate_chip();
|
||||||
|
|
||||||
unsigned int modulated_chip = chip ^ dcf77_bits[current_bit];
|
unsigned int modulated_chip = chip ^ dcf77_bits[current_bit];
|
||||||
|
|
||||||
if (modulated_chip == 0) {
|
if (modulated_chip == 0) {
|
||||||
phase_offset = phase_shift_rad;
|
phase_offset = phase_shift_rad;
|
||||||
} else {
|
} else {
|
||||||
phase_offset = -phase_shift_rad;
|
phase_offset = -phase_shift_rad;
|
||||||
}
|
}
|
||||||
|
|
||||||
current_chip_count++;
|
current_chip_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
current_cycle_count = (current_cycle_count + 1) % CHIP_CYCLES;
|
current_cycle_count = (current_cycle_count + 1) % CHIP_CYCLES;
|
||||||
}
|
}
|
||||||
|
|
||||||
float t = osc.phase + phase_offset;
|
float t = osc.phase + phase_offset;
|
||||||
float carrier = sinf(t);
|
float carrier = sinf(t);
|
||||||
advance_oscillator(&osc);
|
advance_oscillator(&osc);
|
||||||
|
|
||||||
if (transmitting) {
|
if (transmitting) {
|
||||||
if ((dcf77_bits[current_bit] == 0 && ms_within_second < PULSE_0_DURATION) ||
|
if ((dcf77_bits[current_bit] == 0 && ms_within_second < PULSE_0_DURATION) ||
|
||||||
(dcf77_bits[current_bit] == 1 && ms_within_second < PULSE_1_DURATION)) {
|
(dcf77_bits[current_bit] == 1 && ms_within_second < PULSE_1_DURATION)) {
|
||||||
output[i] = carrier * master_volume * REDUCED_AMPLITUDE;
|
output[i] = carrier * master_volume * REDUCED_AMPLITUDE;
|
||||||
} else {
|
} else {
|
||||||
output[i] = carrier * master_volume;
|
output[i] = carrier * master_volume;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
output[i] = carrier * master_volume;
|
output[i] = carrier * master_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
elapsed_samples++;
|
elapsed_samples++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
||||||
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
||||||
to_run = 0;
|
to_run = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Cleaning up...\n");
|
printf("Cleaning up...\n");
|
||||||
pa_simple_free(output_device);
|
pa_simple_free(output_device);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
833
src/fm95.c
833
src/fm95.c
@@ -35,490 +35,487 @@
|
|||||||
#define DEFAULT_MASTER_VOLUME 1.0f // Volume of everything combined, for calibration
|
#define DEFAULT_MASTER_VOLUME 1.0f // Volume of everything combined, for calibration
|
||||||
#define DEFAULT_AUDIO_VOLUME 1.0f // Audio volume, before clipper
|
#define DEFAULT_AUDIO_VOLUME 1.0f // Audio volume, before clipper
|
||||||
|
|
||||||
#define MONO_VOLUME 0.45f // L+R Signal
|
#define MONO_VOLUME 0.45f
|
||||||
#define PILOT_VOLUME 0.09f // 19 KHz Pilot
|
#define PILOT_VOLUME 0.09f
|
||||||
#define STEREO_VOLUME 0.45f // L-R signal, should be same as MONO
|
#define STEREO_VOLUME 0.45f
|
||||||
#define RDS_VOLUME 0.075f // RDS Volume, after dsb-sc
|
#define RDS_VOLUME 0.075f
|
||||||
#define RDS2_VOLUME 0.075f // RDS2 Volume
|
#define RDS2_VOLUME 0.0675f
|
||||||
#define SCA_VOLUME 0.1f // FM SCA signal, 10%
|
#define SCA_VOLUME 0.1f
|
||||||
#define MPX_VOLUME 1.0f // Passtrough
|
#define MPX_VOLUME 1.0f
|
||||||
#define MPX_CLIPPER_THRESHOLD 1.0f
|
#define MPX_CLIPPER_THRESHOLD 1.0f
|
||||||
|
|
||||||
static volatile sig_atomic_t to_run = 1;
|
static volatile sig_atomic_t to_run = 1;
|
||||||
|
|
||||||
void uninterleave(const float *input, float *left, float *right, size_t num_samples) {
|
void uninterleave(const float *input, float *left, float *right, size_t num_samples) {
|
||||||
for (size_t i = 0; i < num_samples/2; i++) {
|
for (size_t i = 0; i < num_samples/2; i++) {
|
||||||
left[i] = input[i * 2];
|
left[i] = input[i * 2];
|
||||||
right[i] = input[i * 2 + 1];
|
right[i] = input[i * 2 + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stop(int signum) {
|
static void stop(int signum) {
|
||||||
(void)signum;
|
(void)signum;
|
||||||
printf("\nReceived stop signal.\n");
|
printf("\nReceived stop signal.\n");
|
||||||
to_run = 0;
|
to_run = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_version() {
|
void show_version() {
|
||||||
printf("fm95 (an FM Processor by radio95) version 1.5\n");
|
printf("fm95 (an FM Processor by radio95) version 1.5\n");
|
||||||
}
|
}
|
||||||
void show_help(char *name) {
|
void show_help(char *name) {
|
||||||
printf(
|
printf(
|
||||||
"Usage: %s\n"
|
"Usage: %s\n"
|
||||||
" -s,--stereo Force Stereo [default: %d]\n"
|
" -s,--stereo Force Stereo [default: %d]\n"
|
||||||
" -i,--input Override input device [default: %s]\n"
|
" -i,--input Override input device [default: %s]\n"
|
||||||
" -o,--output Override output device [default: %s]\n"
|
" -o,--output Override output device [default: %s]\n"
|
||||||
" -M,--mpx Override MPX input device [default: %s]\n"
|
" -M,--mpx Override MPX input device [default: %s]\n"
|
||||||
" -r,--rds Override RDS95 input device [default: %s]\n"
|
" -r,--rds Override RDS95 input device [default: %s]\n"
|
||||||
" -C,--sca Override the SCA input device [default: %s]\n"
|
" -C,--sca Override the SCA input device [default: %s]\n"
|
||||||
" -f,--sca_freq Override the SCA frequency [default: %.1f]\n"
|
" -f,--sca_freq Override the SCA frequency [default: %.1f]\n"
|
||||||
" -F,--sca_dev Override the SCA deviation [default: %.2f]\n"
|
" -F,--sca_dev Override the SCA deviation [default: %.2f]\n"
|
||||||
" -L,--sca_clip Override the SCA clipper threshold [default: %.2f]\n"
|
" -L,--sca_clip Override the SCA clipper threshold [default: %.2f]\n"
|
||||||
" -c,--clipper Override the clipper threshold [default: %.2f]\n"
|
" -c,--clipper Override the clipper threshold [default: %.2f]\n"
|
||||||
" -P,--polar Force Polar Stereo (does not take effect with -m%s)\n"
|
" -P,--polar Force Polar Stereo (does not take effect with -m%s)\n"
|
||||||
" -R,--preemp Override preemphasis [default: %.2f µs]\n"
|
" -R,--preemp Override preemphasis [default: %.2f µs]\n"
|
||||||
" -V,--calibrate Enable Calibration mode [default: off]\n"
|
" -V,--calibrate Enable Calibration mode [default: off]\n"
|
||||||
" -A,--master_vol Set master volume [default: %.3f]\n"
|
" -A,--master_vol Set master volume [default: %.3f]\n"
|
||||||
" -v,--audio_vol Set audio volume [default: %.3f]\n"
|
" -v,--audio_vol Set audio volume [default: %.3f]\n"
|
||||||
,name
|
,name
|
||||||
,DEFAULT_STEREO
|
,DEFAULT_STEREO
|
||||||
,INPUT_DEVICE
|
,INPUT_DEVICE
|
||||||
,OUTPUT_DEVICE
|
,OUTPUT_DEVICE
|
||||||
#ifdef MPX_DEVICE
|
#ifdef MPX_DEVICE
|
||||||
,MPX_DEVICE
|
,MPX_DEVICE
|
||||||
#else
|
#else
|
||||||
,"not set"
|
,"not set"
|
||||||
#endif
|
#endif
|
||||||
#ifdef RDS_DEVICE
|
#ifdef RDS_DEVICE
|
||||||
,RDS_DEVICE
|
,RDS_DEVICE
|
||||||
#else
|
#else
|
||||||
,"not set"
|
,"not set"
|
||||||
#endif
|
#endif
|
||||||
#ifdef SCA_DEVICE
|
#ifdef SCA_DEVICE
|
||||||
,SCA_DEVICE
|
,SCA_DEVICE
|
||||||
#else
|
#else
|
||||||
,"not set"
|
,"not set"
|
||||||
#endif
|
#endif
|
||||||
,DEFAULT_SCA_FREQUENCY
|
,DEFAULT_SCA_FREQUENCY
|
||||||
,DEFAULT_SCA_DEVIATION
|
,DEFAULT_SCA_DEVIATION
|
||||||
,DEFAULT_SCA_CLIPPER_THRESHOLD
|
,DEFAULT_SCA_CLIPPER_THRESHOLD
|
||||||
,DEFAULT_CLIPPER_THRESHOLD
|
,DEFAULT_CLIPPER_THRESHOLD
|
||||||
,(DEFAULT_STEREO_POLAR == 1) ? ", default" : ""
|
,(DEFAULT_STEREO_POLAR == 1) ? ", default" : ""
|
||||||
,DEFAULT_PREEMPHASIS_TAU/0.000001
|
,DEFAULT_PREEMPHASIS_TAU/0.000001
|
||||||
,DEFAULT_MASTER_VOLUME
|
,DEFAULT_MASTER_VOLUME
|
||||||
,DEFAULT_AUDIO_VOLUME
|
,DEFAULT_AUDIO_VOLUME
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
show_version();
|
show_version();
|
||||||
|
|
||||||
pa_simple *mpx_device = NULL;
|
pa_simple *mpx_device = NULL;
|
||||||
pa_simple *rds_device = NULL;
|
pa_simple *rds_device = NULL;
|
||||||
pa_simple *sca_device = NULL;
|
pa_simple *sca_device = NULL;
|
||||||
|
|
||||||
pa_simple *input_device;
|
pa_simple *input_device;
|
||||||
pa_simple *output_device;
|
pa_simple *output_device;
|
||||||
|
|
||||||
float clipper_threshold = DEFAULT_CLIPPER_THRESHOLD;
|
float clipper_threshold = DEFAULT_CLIPPER_THRESHOLD;
|
||||||
int stereo = DEFAULT_STEREO;
|
int stereo = DEFAULT_STEREO;
|
||||||
int polar_stereo = DEFAULT_STEREO_POLAR;
|
int polar_stereo = DEFAULT_STEREO_POLAR;
|
||||||
|
|
||||||
float sca_frequency = DEFAULT_SCA_FREQUENCY;
|
float sca_frequency = DEFAULT_SCA_FREQUENCY;
|
||||||
float sca_deviation = DEFAULT_SCA_DEVIATION;
|
float sca_deviation = DEFAULT_SCA_DEVIATION;
|
||||||
float sca_clipper_threshold = DEFAULT_SCA_CLIPPER_THRESHOLD;
|
float sca_clipper_threshold = DEFAULT_SCA_CLIPPER_THRESHOLD;
|
||||||
|
|
||||||
char audio_input_device[64] = INPUT_DEVICE;
|
char audio_input_device[64] = INPUT_DEVICE;
|
||||||
char audio_output_device[64] = OUTPUT_DEVICE;
|
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||||
#ifndef MPX_DEVICE
|
#ifndef MPX_DEVICE
|
||||||
char audio_mpx_device[64] = "\0";
|
char audio_mpx_device[64] = "\0";
|
||||||
#else
|
#else
|
||||||
char audio_mpx_device[64] = MPX_DEVICE;
|
char audio_mpx_device[64] = MPX_DEVICE;
|
||||||
#endif
|
#endif
|
||||||
#ifndef RDS_DEVICE
|
#ifndef RDS_DEVICE
|
||||||
char audio_rds_device[64] = "\0";
|
char audio_rds_device[64] = "\0";
|
||||||
#else
|
#else
|
||||||
char audio_rds_device[64] = RDS_DEVICE;
|
char audio_rds_device[64] = RDS_DEVICE;
|
||||||
#endif
|
#endif
|
||||||
#ifndef SCA_DEVICE
|
#ifndef SCA_DEVICE
|
||||||
char audio_sca_device[64] = "\0";
|
char audio_sca_device[64] = "\0";
|
||||||
#else
|
#else
|
||||||
char audio_sca_device[64] = SCA_DEVICE;
|
char audio_sca_device[64] = SCA_DEVICE;
|
||||||
#endif
|
#endif
|
||||||
float preemphasis_tau = DEFAULT_PREEMPHASIS_TAU;
|
float preemphasis_tau = DEFAULT_PREEMPHASIS_TAU;
|
||||||
|
|
||||||
int calibration_mode = 0;
|
int calibration_mode = 0;
|
||||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
float master_volume = DEFAULT_MASTER_VOLUME;
|
||||||
float audio_volume = DEFAULT_AUDIO_VOLUME;
|
float audio_volume = DEFAULT_AUDIO_VOLUME;
|
||||||
|
|
||||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
int sample_rate = DEFAULT_SAMPLE_RATE;
|
||||||
|
|
||||||
// #region Parse Arguments
|
// #region Parse Arguments
|
||||||
int opt;
|
int opt;
|
||||||
const char *short_opt = "s::i:o:M:r:C:f:F:L:c:P::R:VA:v:h";
|
const char *short_opt = "s::i:o:M:r:C:f:F:L:c:P::R:VA:v:h";
|
||||||
struct option long_opt[] =
|
struct option long_opt[] =
|
||||||
{
|
{
|
||||||
{"stereo", optional_argument, NULL, 's'},
|
{"stereo", optional_argument, NULL, 's'},
|
||||||
{"input", required_argument, NULL, 'i'},
|
{"input", required_argument, NULL, 'i'},
|
||||||
{"output", required_argument, NULL, 'o'},
|
{"output", required_argument, NULL, 'o'},
|
||||||
{"mpx", required_argument, NULL, 'M'},
|
{"mpx", required_argument, NULL, 'M'},
|
||||||
{"rds", required_argument, NULL, 'r'},
|
{"rds", required_argument, NULL, 'r'},
|
||||||
{"sca", required_argument, NULL, 'C'},
|
{"sca", required_argument, NULL, 'C'},
|
||||||
{"sca_freq", required_argument, NULL, 'f'},
|
{"sca_freq", required_argument, NULL, 'f'},
|
||||||
{"sca_dev", required_argument, NULL, 'F'},
|
{"sca_dev", required_argument, NULL, 'F'},
|
||||||
{"sca_clip", required_argument, NULL, 'L'},
|
{"sca_clip", required_argument, NULL, 'L'},
|
||||||
{"clipper", required_argument, NULL, 'c'},
|
{"clipper", required_argument, NULL, 'c'},
|
||||||
{"polar", optional_argument, NULL, 'P'},
|
{"polar", optional_argument, NULL, 'P'},
|
||||||
{"preemp", required_argument, NULL, 'R'},
|
{"preemp", required_argument, NULL, 'R'},
|
||||||
{"calibrate", no_argument, NULL, 'V'},
|
{"calibrate", no_argument, NULL, 'V'},
|
||||||
{"master_vol", required_argument, NULL, 'A'},
|
{"master_vol", required_argument, NULL, 'A'},
|
||||||
{"audio_vol", required_argument, NULL, 'v'},
|
{"audio_vol", required_argument, NULL, 'v'},
|
||||||
|
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||||
switch(opt) {
|
switch(opt) {
|
||||||
case 's': // Stereo
|
case 's': // Stereo
|
||||||
if(optarg) {
|
if(optarg) {
|
||||||
stereo = atoi(optarg);
|
stereo = atoi(optarg);
|
||||||
} else {
|
} else {
|
||||||
stereo = 1;
|
stereo = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'i': // Input Device
|
case 'i': // Input Device
|
||||||
memcpy(audio_input_device, optarg, 63);
|
memcpy(audio_input_device, optarg, 63);
|
||||||
break;
|
break;
|
||||||
case 'o': // Output Device
|
case 'o': // Output Device
|
||||||
memcpy(audio_output_device, optarg, 63);
|
memcpy(audio_output_device, optarg, 63);
|
||||||
break;;
|
break;;
|
||||||
case 'M': //MPX in
|
case 'M': //MPX in
|
||||||
memcpy(audio_mpx_device, optarg, 63);
|
memcpy(audio_mpx_device, optarg, 63);
|
||||||
break;
|
break;
|
||||||
case 'r': // RDS in
|
case 'r': // RDS in
|
||||||
memcpy(audio_rds_device, optarg, 63);
|
memcpy(audio_rds_device, optarg, 63);
|
||||||
break;
|
break;
|
||||||
case 'C': //SCA in
|
case 'C': //SCA in
|
||||||
memcpy(audio_sca_device, optarg, 63);
|
memcpy(audio_sca_device, optarg, 63);
|
||||||
break;
|
break;
|
||||||
case 'f': //SCA freq
|
case 'f': //SCA freq
|
||||||
sca_frequency = strtof(optarg, NULL);
|
sca_frequency = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 'F': //SCA deviation
|
case 'F': //SCA deviation
|
||||||
sca_deviation = strtof(optarg, NULL);
|
sca_deviation = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 'L': //SCA clip
|
case 'L': //SCA clip
|
||||||
sca_clipper_threshold = strtof(optarg, NULL);
|
sca_clipper_threshold = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 'c': //Clipper
|
case 'c': //Clipper
|
||||||
clipper_threshold = strtof(optarg, NULL);
|
clipper_threshold = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 'P': //Polar
|
case 'P': //Polar
|
||||||
if(optarg) {
|
if(optarg) {
|
||||||
polar_stereo = atoi(optarg);
|
polar_stereo = atoi(optarg);
|
||||||
} else {
|
} else {
|
||||||
polar_stereo = 1;
|
polar_stereo = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'R': // Preemp
|
case 'R': // Preemp
|
||||||
preemphasis_tau = strtof(optarg, NULL)*0.000001;
|
preemphasis_tau = strtof(optarg, NULL)*0.000001;
|
||||||
break;
|
break;
|
||||||
case 'V': // Calibration
|
case 'V': // Calibration
|
||||||
calibration_mode = 1;
|
calibration_mode = 1;
|
||||||
break;
|
break;
|
||||||
case 'A': // Master vol
|
case 'A': // Master vol
|
||||||
master_volume = strtof(optarg, NULL);
|
master_volume = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 'v': // Audio Volume
|
case 'v': // Audio Volume
|
||||||
audio_volume = strtof(optarg, NULL);
|
audio_volume = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
show_help(argv[0]);
|
show_help(argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
int mpx_on = (strlen(audio_mpx_device) != 0);
|
int mpx_on = (strlen(audio_mpx_device) != 0);
|
||||||
int rds_on = (strlen(audio_rds_device) != 0);
|
int rds_on = (strlen(audio_rds_device) != 0);
|
||||||
int sca_on = (strlen(audio_sca_device) != 0);
|
int sca_on = (strlen(audio_sca_device) != 0);
|
||||||
|
|
||||||
// #region Setup devices
|
// #region Setup devices
|
||||||
|
|
||||||
// Define formats and buffer atributes
|
// Define formats and buffer atributes
|
||||||
pa_sample_spec stereo_format = {
|
pa_sample_spec stereo_format = {
|
||||||
.format = PA_SAMPLE_FLOAT32NE,
|
.format = PA_SAMPLE_FLOAT32NE,
|
||||||
.channels = 2,
|
.channels = 2,
|
||||||
.rate = sample_rate
|
.rate = sample_rate
|
||||||
};
|
};
|
||||||
pa_sample_spec mono_format = {
|
pa_sample_spec mono_format = {
|
||||||
.format = PA_SAMPLE_FLOAT32NE,
|
.format = PA_SAMPLE_FLOAT32NE,
|
||||||
.channels = 1,
|
.channels = 1,
|
||||||
.rate = sample_rate
|
.rate = sample_rate
|
||||||
};
|
};
|
||||||
|
|
||||||
pa_buffer_attr input_buffer_atr = {
|
pa_buffer_attr input_buffer_atr = {
|
||||||
.maxlength = buffer_maxlength,
|
.maxlength = buffer_maxlength,
|
||||||
.fragsize = buffer_tlength_fragsize
|
.fragsize = buffer_tlength_fragsize
|
||||||
};
|
};
|
||||||
pa_buffer_attr output_buffer_atr = {
|
pa_buffer_attr output_buffer_atr = {
|
||||||
.maxlength = buffer_maxlength,
|
.maxlength = buffer_maxlength,
|
||||||
.tlength = buffer_tlength_fragsize,
|
.tlength = buffer_tlength_fragsize,
|
||||||
.prebuf = buffer_prebuf
|
.prebuf = buffer_prebuf
|
||||||
};
|
};
|
||||||
|
|
||||||
int opentime_pulse_error;
|
int opentime_pulse_error;
|
||||||
|
|
||||||
printf("Connecting to input device... (%s)\n", audio_input_device);
|
printf("Connecting to input device... (%s)\n", audio_input_device);
|
||||||
|
|
||||||
input_device = pa_simple_new(
|
input_device = pa_simple_new(
|
||||||
NULL,
|
NULL,
|
||||||
"fm95",
|
"fm95",
|
||||||
PA_STREAM_RECORD,
|
PA_STREAM_RECORD,
|
||||||
audio_input_device,
|
audio_input_device,
|
||||||
"Main Audio Input",
|
"Main Audio Input",
|
||||||
&stereo_format,
|
&stereo_format,
|
||||||
NULL,
|
NULL,
|
||||||
&input_buffer_atr,
|
&input_buffer_atr,
|
||||||
&opentime_pulse_error
|
&opentime_pulse_error
|
||||||
);
|
);
|
||||||
if (!input_device) {
|
if (!input_device) {
|
||||||
fprintf(stderr, "Error: cannot open input device: %s\n", pa_strerror(opentime_pulse_error));
|
fprintf(stderr, "Error: cannot open input device: %s\n", pa_strerror(opentime_pulse_error));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mpx_on) {
|
if(mpx_on) {
|
||||||
printf("Connecting to MPX device... (%s)\n", audio_mpx_device);
|
printf("Connecting to MPX device... (%s)\n", audio_mpx_device);
|
||||||
|
|
||||||
mpx_device = pa_simple_new(
|
mpx_device = pa_simple_new(
|
||||||
NULL,
|
NULL,
|
||||||
"fm95",
|
"fm95",
|
||||||
PA_STREAM_RECORD,
|
PA_STREAM_RECORD,
|
||||||
audio_mpx_device,
|
audio_mpx_device,
|
||||||
"MPX Input",
|
"MPX Input",
|
||||||
&mono_format,
|
&mono_format,
|
||||||
NULL,
|
NULL,
|
||||||
&input_buffer_atr,
|
&input_buffer_atr,
|
||||||
&opentime_pulse_error
|
&opentime_pulse_error
|
||||||
);
|
);
|
||||||
if (!mpx_device) {
|
if (!mpx_device) {
|
||||||
fprintf(stderr, "Error: cannot open MPX device: %s\n", pa_strerror(opentime_pulse_error));
|
fprintf(stderr, "Error: cannot open MPX device: %s\n", pa_strerror(opentime_pulse_error));
|
||||||
pa_simple_free(input_device);
|
pa_simple_free(input_device);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(rds_on) {
|
if(rds_on) {
|
||||||
printf("Connecting to RDS95 device... (%s)\n", audio_rds_device);
|
printf("Connecting to RDS95 device... (%s)\n", audio_rds_device);
|
||||||
|
|
||||||
rds_device = pa_simple_new(
|
rds_device = pa_simple_new(
|
||||||
NULL,
|
NULL,
|
||||||
"fm95",
|
"fm95",
|
||||||
PA_STREAM_RECORD,
|
PA_STREAM_RECORD,
|
||||||
audio_rds_device,
|
audio_rds_device,
|
||||||
"RDS Input",
|
"RDS Input",
|
||||||
&stereo_format,
|
&stereo_format,
|
||||||
NULL,
|
NULL,
|
||||||
&input_buffer_atr,
|
&input_buffer_atr,
|
||||||
&opentime_pulse_error
|
&opentime_pulse_error
|
||||||
);
|
);
|
||||||
if (!rds_device) {
|
if (!rds_device) {
|
||||||
fprintf(stderr, "Error: cannot open RDS device: %s\n", pa_strerror(opentime_pulse_error));
|
fprintf(stderr, "Error: cannot open RDS device: %s\n", pa_strerror(opentime_pulse_error));
|
||||||
pa_simple_free(input_device);
|
pa_simple_free(input_device);
|
||||||
if(mpx_on) pa_simple_free(mpx_device);
|
if(mpx_on) pa_simple_free(mpx_device);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sca_on) {
|
if(sca_on) {
|
||||||
printf("Connecting to SCA device... (%s)\n", audio_sca_device);
|
printf("Connecting to SCA device... (%s)\n", audio_sca_device);
|
||||||
|
|
||||||
sca_device = pa_simple_new(
|
sca_device = pa_simple_new(
|
||||||
NULL,
|
NULL,
|
||||||
"fm95",
|
"fm95",
|
||||||
PA_STREAM_RECORD,
|
PA_STREAM_RECORD,
|
||||||
audio_sca_device,
|
audio_sca_device,
|
||||||
"SCA Input",
|
"SCA Input",
|
||||||
&mono_format,
|
&mono_format,
|
||||||
NULL,
|
NULL,
|
||||||
&input_buffer_atr,
|
&input_buffer_atr,
|
||||||
&opentime_pulse_error
|
&opentime_pulse_error
|
||||||
);
|
);
|
||||||
if (!sca_device) {
|
if (!sca_device) {
|
||||||
fprintf(stderr, "Error: cannot open SCA device: %s\n", pa_strerror(opentime_pulse_error));
|
fprintf(stderr, "Error: cannot open SCA device: %s\n", pa_strerror(opentime_pulse_error));
|
||||||
pa_simple_free(input_device);
|
pa_simple_free(input_device);
|
||||||
if(mpx_on) pa_simple_free(mpx_device);
|
if(mpx_on) pa_simple_free(mpx_device);
|
||||||
if(rds_on) pa_simple_free(rds_device);
|
if(rds_on) pa_simple_free(rds_device);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
printf("Connecting to output device... (%s)\n", audio_output_device);
|
||||||
|
|
||||||
output_device = pa_simple_new(
|
output_device = pa_simple_new(
|
||||||
NULL,
|
NULL,
|
||||||
"StereoEncoder",
|
"StereoEncoder",
|
||||||
PA_STREAM_PLAYBACK,
|
PA_STREAM_PLAYBACK,
|
||||||
audio_output_device,
|
audio_output_device,
|
||||||
"MPX Output",
|
"MPX Output",
|
||||||
&mono_format,
|
&mono_format,
|
||||||
NULL,
|
NULL,
|
||||||
&output_buffer_atr,
|
&output_buffer_atr,
|
||||||
&opentime_pulse_error
|
&opentime_pulse_error
|
||||||
);
|
);
|
||||||
if (!output_device) {
|
if (!output_device) {
|
||||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(opentime_pulse_error));
|
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(opentime_pulse_error));
|
||||||
pa_simple_free(input_device);
|
pa_simple_free(input_device);
|
||||||
if(mpx_on) pa_simple_free(mpx_device);
|
if(mpx_on) pa_simple_free(mpx_device);
|
||||||
if(rds_on) pa_simple_free(rds_device);
|
if(rds_on) pa_simple_free(rds_device);
|
||||||
if(sca_on) pa_simple_free(sca_device);
|
if(sca_on) pa_simple_free(sca_device);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
if(calibration_mode) {
|
if(calibration_mode) {
|
||||||
Oscillator osc;
|
Oscillator osc;
|
||||||
init_oscillator(&osc, 400, sample_rate);
|
init_oscillator(&osc, 400, sample_rate);
|
||||||
|
|
||||||
signal(SIGINT, stop);
|
signal(SIGINT, stop);
|
||||||
signal(SIGTERM, stop);
|
signal(SIGTERM, stop);
|
||||||
int pulse_error;
|
int pulse_error;
|
||||||
float output[BUFFER_SIZE];
|
float output[BUFFER_SIZE];
|
||||||
|
|
||||||
while(to_run) {
|
while(to_run) {
|
||||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||||
output[i] = get_oscillator_sin_sample(&osc)*master_volume;
|
output[i] = get_oscillator_sin_sample(&osc)*master_volume;
|
||||||
}
|
}
|
||||||
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
||||||
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
||||||
to_run = 0;
|
to_run = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("Cleaning up...\n");
|
printf("Cleaning up...\n");
|
||||||
pa_simple_free(input_device);
|
pa_simple_free(input_device);
|
||||||
if(mpx_on) pa_simple_free(mpx_device);
|
if(mpx_on) pa_simple_free(mpx_device);
|
||||||
if(rds_on) pa_simple_free(rds_device);
|
if(rds_on) pa_simple_free(rds_device);
|
||||||
if(sca_on) pa_simple_free(sca_device);
|
if(sca_on) pa_simple_free(sca_device);
|
||||||
pa_simple_free(output_device);
|
pa_simple_free(output_device);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region Setup Filters/Modulaltors/Oscillators
|
// #region Setup Filters/Modulaltors/Oscillators
|
||||||
Oscillator osc;
|
Oscillator osc;
|
||||||
init_oscillator(&osc, polar_stereo ? 31250.0 : 19000, sample_rate);
|
init_oscillator(&osc, polar_stereo ? 31250.0 : 19000, sample_rate);
|
||||||
|
|
||||||
Oscillator rds2_osc;
|
FMModulator sca_mod;
|
||||||
init_oscillator(&rds2_osc, 66500, sample_rate);
|
init_fm_modulator(&sca_mod, sca_frequency, sca_deviation, sample_rate);
|
||||||
|
|
||||||
FMModulator sca_mod;
|
ResistorCapacitor preemp_l, preemp_r;
|
||||||
init_fm_modulator(&sca_mod, sca_frequency, sca_deviation, sample_rate);
|
init_preemphasis(&preemp_l, preemphasis_tau, sample_rate);
|
||||||
|
init_preemphasis(&preemp_r, preemphasis_tau, sample_rate);
|
||||||
|
|
||||||
ResistorCapacitor preemp_l, preemp_r;
|
PLL rds2_pll;
|
||||||
init_preemphasis(&preemp_l, preemphasis_tau, sample_rate);
|
init_pll(&rds2_pll, 7, 2, 19000, 100, 1, sample_rate);
|
||||||
init_preemphasis(&preemp_r, preemphasis_tau, sample_rate);
|
// #endregion
|
||||||
|
|
||||||
PLL rds2_pll;
|
signal(SIGINT, stop);
|
||||||
init_pll(&rds2_pll, 66500, 19000, 100, 1, sample_rate);
|
signal(SIGTERM, stop);
|
||||||
// #endregion
|
|
||||||
|
|
||||||
signal(SIGINT, stop);
|
int pulse_error;
|
||||||
signal(SIGTERM, stop);
|
|
||||||
|
|
||||||
int pulse_error;
|
float audio_stereo_input[BUFFER_SIZE*2];
|
||||||
|
|
||||||
float audio_stereo_input[BUFFER_SIZE*2];
|
float rds1_rds2_in[BUFFER_SIZE*2] = {0};
|
||||||
|
float rds1_in[BUFFER_SIZE] = {0};
|
||||||
|
float rds2_in[BUFFER_SIZE] = {0};
|
||||||
|
|
||||||
float rds1_rds2_in[BUFFER_SIZE*2] = {0};
|
float mpx_in[BUFFER_SIZE] = {0};
|
||||||
float rds1_in[BUFFER_SIZE] = {0};
|
float sca_in[BUFFER_SIZE] = {0};
|
||||||
float rds2_in[BUFFER_SIZE] = {0};
|
float left[BUFFER_SIZE], right[BUFFER_SIZE];
|
||||||
|
float output[BUFFER_SIZE];
|
||||||
|
while (to_run) {
|
||||||
|
if (pa_simple_read(input_device, audio_stereo_input, sizeof(audio_stereo_input), &pulse_error) < 0) {
|
||||||
|
fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
|
||||||
|
to_run = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uninterleave(audio_stereo_input, left, right, BUFFER_SIZE*2);
|
||||||
|
if(mpx_on) {
|
||||||
|
if (pa_simple_read(mpx_device, mpx_in, sizeof(mpx_in), &pulse_error) < 0) {
|
||||||
|
fprintf(stderr, "Error reading from MPX device: %s\n", pa_strerror(pulse_error));
|
||||||
|
to_run = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rds_on) {
|
||||||
|
if (pa_simple_read(rds_device, rds1_rds2_in, sizeof(rds1_rds2_in), &pulse_error) < 0) {
|
||||||
|
fprintf(stderr, "Error reading from RDS95 device: %s\n", pa_strerror(pulse_error));
|
||||||
|
to_run = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uninterleave(rds1_rds2_in, rds1_in, rds2_in, BUFFER_SIZE*2);
|
||||||
|
}
|
||||||
|
if(sca_on) {
|
||||||
|
if (pa_simple_read(sca_device, sca_in, sizeof(sca_in), &pulse_error) < 0) {
|
||||||
|
fprintf(stderr, "Error reading from SCA device: %s\n", pa_strerror(pulse_error));
|
||||||
|
to_run = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float mpx_in[BUFFER_SIZE] = {0};
|
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||||
float sca_in[BUFFER_SIZE] = {0};
|
float l_in = left[i];
|
||||||
float left[BUFFER_SIZE], right[BUFFER_SIZE];
|
float r_in = right[i];
|
||||||
float output[BUFFER_SIZE];
|
float current_mpx_in = mpx_in[i];
|
||||||
while (to_run) {
|
float current_rds_in = rds1_in[i];
|
||||||
if (pa_simple_read(input_device, audio_stereo_input, sizeof(audio_stereo_input), &pulse_error) < 0) {
|
float current_rds2_in = rds2_in[i];
|
||||||
fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
|
float current_sca_in = sca_in[i];
|
||||||
to_run = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
uninterleave(audio_stereo_input, left, right, BUFFER_SIZE*2);
|
|
||||||
if(mpx_on) {
|
|
||||||
if (pa_simple_read(mpx_device, mpx_in, sizeof(mpx_in), &pulse_error) < 0) {
|
|
||||||
fprintf(stderr, "Error reading from MPX device: %s\n", pa_strerror(pulse_error));
|
|
||||||
to_run = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rds_on) {
|
|
||||||
if (pa_simple_read(rds_device, rds1_rds2_in, sizeof(rds1_rds2_in), &pulse_error) < 0) {
|
|
||||||
fprintf(stderr, "Error reading from RDS95 device: %s\n", pa_strerror(pulse_error));
|
|
||||||
to_run = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
uninterleave(rds1_rds2_in, rds1_in, rds2_in, BUFFER_SIZE*2);
|
|
||||||
}
|
|
||||||
if(sca_on) {
|
|
||||||
if (pa_simple_read(sca_device, sca_in, sizeof(sca_in), &pulse_error) < 0) {
|
|
||||||
fprintf(stderr, "Error reading from SCA device: %s\n", pa_strerror(pulse_error));
|
|
||||||
to_run = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
float ready_l = apply_preemphasis(&preemp_l, l_in)*2;
|
||||||
float l_in = left[i];
|
float ready_r = apply_preemphasis(&preemp_r, r_in)*2;
|
||||||
float r_in = right[i];
|
ready_l = hard_clip(ready_l*audio_volume, clipper_threshold);
|
||||||
float current_mpx_in = mpx_in[i];
|
ready_r = hard_clip(ready_r*audio_volume, clipper_threshold);
|
||||||
float current_rds_in = rds1_in[i];
|
|
||||||
float current_rds2_in = rds2_in[i];
|
|
||||||
float current_sca_in = sca_in[i];
|
|
||||||
|
|
||||||
float ready_l = apply_preemphasis(&preemp_l, l_in)*2;
|
float mono = (ready_l + ready_r) / 2.0f;
|
||||||
float ready_r = apply_preemphasis(&preemp_r, r_in)*2;
|
output[i] = mono*MONO_VOLUME;
|
||||||
ready_l = hard_clip(ready_l*audio_volume, clipper_threshold);
|
if(stereo) {
|
||||||
ready_r = hard_clip(ready_r*audio_volume, clipper_threshold);
|
float stereo = (ready_l - ready_r) / 2.0f;
|
||||||
|
float stereo_carrier = get_oscillator_sin_multiplier_ni(&osc, polar_stereo ? 1 : 2);
|
||||||
|
|
||||||
float mono = (ready_l + ready_r) / 2.0f;
|
if(polar_stereo) {
|
||||||
output[i] = mono*MONO_VOLUME;
|
output[i] += ((stereo+0.2)*stereo_carrier)*STEREO_VOLUME;
|
||||||
if(stereo) {
|
} else {
|
||||||
float stereo = (ready_l - ready_r) / 2.0f;
|
float pilot = get_oscillator_sin_multiplier_ni(&osc, 1);
|
||||||
float stereo_carrier = get_oscillator_sin_multiplier_ni(&osc, polar_stereo ? 1 : 2);
|
output[i] += pilot*PILOT_VOLUME +
|
||||||
|
(stereo*stereo_carrier)*STEREO_VOLUME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rds_on && polar_stereo == 0) {
|
||||||
|
float rds_carrier = get_oscillator_sin_multiplier_ni(&osc, 3);
|
||||||
|
output[i] += (current_rds_in*rds_carrier)*RDS_VOLUME;
|
||||||
|
if(!sca_on) {
|
||||||
|
float rds2_carrier_66 = apply_pll(&rds2_pll, get_oscillator_sin_multiplier_ni(&osc, 1));
|
||||||
|
output[i] += (current_rds2_in*rds2_carrier_66)*RDS2_VOLUME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(rds_on || stereo) advance_oscillator(&osc);
|
||||||
|
if(mpx_on) output[i] += hard_clip(current_mpx_in, MPX_CLIPPER_THRESHOLD)*MPX_VOLUME;
|
||||||
|
if(sca_on) output[i] += modulate_fm(&sca_mod, hard_clip(current_sca_in, sca_clipper_threshold))*SCA_VOLUME;
|
||||||
|
output[i] *= master_volume;
|
||||||
|
}
|
||||||
|
|
||||||
if(polar_stereo) {
|
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
||||||
output[i] += ((stereo+0.2)*stereo_carrier)*STEREO_VOLUME;
|
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
||||||
} else {
|
to_run = 0;
|
||||||
float pilot = get_oscillator_sin_multiplier_ni(&osc, 1);
|
break;
|
||||||
output[i] += pilot*PILOT_VOLUME +
|
}
|
||||||
(stereo*stereo_carrier)*STEREO_VOLUME;
|
}
|
||||||
}
|
printf("Cleaning up...\n");
|
||||||
}
|
pa_simple_free(input_device);
|
||||||
if(rds_on && polar_stereo == 0) {
|
if(mpx_on) pa_simple_free(mpx_device);
|
||||||
float rds_carrier = get_oscillator_sin_multiplier_ni(&osc, 3);
|
if(rds_on) pa_simple_free(rds_device);
|
||||||
output[i] += (current_rds_in*rds_carrier)*RDS_VOLUME;
|
if(sca_on) pa_simple_free(sca_device);
|
||||||
if(!sca_on) {
|
pa_simple_free(output_device);
|
||||||
float rds2_carrier_66 = apply_pll(&rds2_pll, get_oscillator_sin_multiplier_ni(&osc, 1), get_oscillator_cos_sample(&rds2_osc));
|
return 0;
|
||||||
output[i] += (current_rds2_in*rds2_carrier_66)*RDS2_VOLUME;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(rds_on || stereo) advance_oscillator(&osc);
|
|
||||||
if(mpx_on) output[i] += hard_clip(current_mpx_in, MPX_CLIPPER_THRESHOLD)*MPX_VOLUME;
|
|
||||||
if(sca_on) output[i] += modulate_fm(&sca_mod, hard_clip(current_sca_in, sca_clipper_threshold))*SCA_VOLUME;
|
|
||||||
output[i] *= master_volume;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
|
||||||
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
|
||||||
to_run = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("Cleaning up...\n");
|
|
||||||
pa_simple_free(input_device);
|
|
||||||
if(mpx_on) pa_simple_free(mpx_device);
|
|
||||||
if(rds_on) pa_simple_free(rds_device);
|
|
||||||
if(sca_on) pa_simple_free(sca_device);
|
|
||||||
pa_simple_free(output_device);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user