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