mirror of
https://github.com/radio95-rnt/fm95.git
synced 2026-07-30 08:19:14 +02:00
rds input via ipc - experimental (hence it is on 61.75 - the only unused frequncy
This commit is contained in:
@@ -0,0 +1,37 @@
|
|||||||
|
// bit_ring.h
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *bits; // one bit stored per byte (0/1) — throughput is tiny, simplicity wins
|
||||||
|
size_t capacity;
|
||||||
|
_Atomic size_t head, tail;
|
||||||
|
} bit_ring_t;
|
||||||
|
|
||||||
|
static inline void bit_ring_init(bit_ring_t *r, size_t capacity) {
|
||||||
|
r->bits = calloc(capacity, 1);
|
||||||
|
r->capacity = capacity;
|
||||||
|
atomic_store(&r->head, 0);
|
||||||
|
atomic_store(&r->tail, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t bit_ring_write(bit_ring_t *r, const uint8_t *bits, size_t n) {
|
||||||
|
size_t head = atomic_load_explicit(&r->head, memory_order_relaxed);
|
||||||
|
size_t tail = atomic_load_explicit(&r->tail, memory_order_acquire);
|
||||||
|
size_t free_space = r->capacity - (head - tail);
|
||||||
|
size_t to_write = n < free_space ? n : free_space;
|
||||||
|
for (size_t i = 0; i < to_write; i++)
|
||||||
|
r->bits[(head + i) % r->capacity] = bits[i];
|
||||||
|
atomic_store_explicit(&r->head, head + to_write, memory_order_release);
|
||||||
|
return to_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns 1 if a bit was available, 0 on underrun
|
||||||
|
static inline int bit_ring_read1(bit_ring_t *r, uint8_t *out) {
|
||||||
|
size_t tail = atomic_load_explicit(&r->tail, memory_order_relaxed);
|
||||||
|
size_t head = atomic_load_explicit(&r->head, memory_order_acquire);
|
||||||
|
if (head == tail) return 0;
|
||||||
|
*out = r->bits[tail % r->capacity];
|
||||||
|
atomic_store_explicit(&r->tail, tail + 1, memory_order_release);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
@@ -6,8 +6,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
|
||||||
static int make_server_socket(const char *path)
|
static int make_server_socket(const char *path) {
|
||||||
{
|
|
||||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
if (fd < 0) { perror("ipc: socket"); return -1; }
|
if (fd < 0) { perror("ipc: socket"); return -1; }
|
||||||
|
|
||||||
@@ -63,9 +62,7 @@ static void *listener_thread(void *arg) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int create_ipc(ipc_ctx_t *ctx, ipc_client_fn handler,
|
int create_ipc(ipc_ctx_t *ctx, ipc_client_fn handler, const char *socket_path, void *user_data) {
|
||||||
const char *socket_path, void *user_data)
|
|
||||||
{
|
|
||||||
ctx->handler = handler;
|
ctx->handler = handler;
|
||||||
ctx->user_data = user_data;
|
ctx->user_data = user_data;
|
||||||
ctx->running = 1;
|
ctx->running = 1;
|
||||||
|
|||||||
+5
-2
@@ -30,7 +30,10 @@ float get_oscillator_cos_multiplier_ni(Oscillator *osc, float multiplier) {
|
|||||||
return cosf(osc->phase * multiplier);
|
return cosf(osc->phase * multiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void advance_oscillator(Oscillator *osc) {
|
inline bool advance_oscillator(Oscillator *osc) {
|
||||||
osc->phase += osc->phase_increment;
|
osc->phase += osc->phase_increment;
|
||||||
if (osc->phase >= M_2PI) osc->phase -= M_2PI;
|
if (osc->phase >= M_2PI) {
|
||||||
|
osc->phase -= M_2PI;
|
||||||
|
return true;
|
||||||
|
} return false;
|
||||||
}
|
}
|
||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
#include <stdbool.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -15,4 +16,4 @@ float get_oscillator_sin_sample(Oscillator *osc);
|
|||||||
float get_oscillator_cos_sample(Oscillator *osc);
|
float get_oscillator_cos_sample(Oscillator *osc);
|
||||||
float get_oscillator_sin_multiplier_ni(Oscillator *osc, float multiplier);
|
float get_oscillator_sin_multiplier_ni(Oscillator *osc, float multiplier);
|
||||||
float get_oscillator_cos_multiplier_ni(Oscillator *osc, float multiplier);
|
float get_oscillator_cos_multiplier_ni(Oscillator *osc, float multiplier);
|
||||||
void advance_oscillator(Oscillator *osc);
|
bool advance_oscillator(Oscillator *osc);
|
||||||
+42
-6
@@ -13,6 +13,7 @@
|
|||||||
#include "stereo_encoder.h"
|
#include "stereo_encoder.h"
|
||||||
#include "bs412.h"
|
#include "bs412.h"
|
||||||
#include "gain_control.h"
|
#include "gain_control.h"
|
||||||
|
#include "bit_ring.h"
|
||||||
|
|
||||||
#define BUFFER_SIZE 4998 // This defines how many samples to process at a time, because the loop here is this: get signal -> process signal -> output signal, and when we get signal we actually get BUFFER_SIZE of them
|
#define BUFFER_SIZE 4998 // This defines how many samples to process at a time, because the loop here is this: get signal -> process signal -> output signal, and when we get signal we actually get BUFFER_SIZE of them
|
||||||
|
|
||||||
@@ -83,6 +84,10 @@ typedef struct {
|
|||||||
StereoEncoder stencode;
|
StereoEncoder stencode;
|
||||||
AGC agc;
|
AGC agc;
|
||||||
delay_line_t rds_delays[4];
|
delay_line_t rds_delays[4];
|
||||||
|
bit_ring_t rds_bitring[1];
|
||||||
|
double rds_phase[1];
|
||||||
|
float rds_symbol[1];
|
||||||
|
uint8_t rds_last_bit[1];
|
||||||
} FM95_Runtime;
|
} FM95_Runtime;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -142,6 +147,8 @@ void cleanup_runtime(FM95_Runtime* runtime, const FM95_Config config) {
|
|||||||
if(runtime->lpf_r != NULL) iirfilt_rrrf_destroy(runtime->lpf_r);
|
if(runtime->lpf_r != NULL) iirfilt_rrrf_destroy(runtime->lpf_r);
|
||||||
runtime->lpf_l = runtime->lpf_r = NULL;
|
runtime->lpf_l = runtime->lpf_r = NULL;
|
||||||
} exit_stereo_encoder(&runtime->stencode);
|
} exit_stereo_encoder(&runtime->stencode);
|
||||||
|
|
||||||
|
free(runtime->rds_bitring[0].bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup_audio_runtime(FM95_Runtime *rt, const FM95_Options options) {
|
void cleanup_audio_runtime(FM95_Runtime *rt, const FM95_Options options) {
|
||||||
@@ -208,6 +215,7 @@ int run_fm95(FM95_Config* config, FM95_Runtime* runtime, FM95_RunResult* result)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(uint16_t i = 0; i < BUFFER_SIZE; i++) {
|
for(uint16_t i = 0; i < BUFFER_SIZE; i++) {
|
||||||
|
bool cycle = advance_oscillator(&runtime->osc);
|
||||||
bool do_result = (i == BUFFER_SIZE - 1);
|
bool do_result = (i == BUFFER_SIZE - 1);
|
||||||
|
|
||||||
float mpx = 0.0f;
|
float mpx = 0.0f;
|
||||||
@@ -251,7 +259,7 @@ int run_fm95(FM95_Config* config, FM95_Runtime* runtime, FM95_RunResult* result)
|
|||||||
uint8_t osc_stream = 12 + stream;
|
uint8_t osc_stream = 12 + stream;
|
||||||
if(osc_stream >= 13) osc_stream++;
|
if(osc_stream >= 13) osc_stream++;
|
||||||
|
|
||||||
float carrier = get_oscillator_cos_multiplier_ni(&runtime->osc, osc_stream);
|
float carrier = get_oscillator_cos_multiplier_ni(&runtime->osc, osc_stream * 4.0f);
|
||||||
if(config->stereo_ssb) carrier = delay_line(&runtime->rds_delays[stream], carrier);
|
if(config->stereo_ssb) carrier = delay_line(&runtime->rds_delays[stream], carrier);
|
||||||
mpx += (runtime->rds_in[config->rds_streams * i + stream] * carrier) * rds_level;
|
mpx += (runtime->rds_in[config->rds_streams * i + stream] * carrier) * rds_level;
|
||||||
|
|
||||||
@@ -259,12 +267,24 @@ int run_fm95(FM95_Config* config, FM95_Runtime* runtime, FM95_RunResult* result)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
if (cycle) {
|
||||||
|
uint8_t bit;
|
||||||
|
if (bit_ring_read1(&runtime->rds_bitring[0], &bit)) {
|
||||||
|
runtime->rds_last_bit[0] = bit;
|
||||||
|
}
|
||||||
|
runtime->rds_symbol[0] = runtime->rds_last_bit[0] ? 1.0f : -1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float clock = get_oscillator_cos_multiplier_ni(&runtime->osc, 1.0f);
|
||||||
|
float carrier = get_oscillator_cos_multiplier_ni(&runtime->osc, 52.0f);
|
||||||
|
mpx += runtime->rds_symbol[0] * clock * carrier * config->volumes.rds;
|
||||||
|
}
|
||||||
|
|
||||||
mpx = bs412_compress(&runtime->bs412, audio, mpx+mpx_in[i], &temp_result.mpx_power);
|
mpx = bs412_compress(&runtime->bs412, audio, mpx+mpx_in[i], &temp_result.mpx_power);
|
||||||
temp_result.bs412_gain = runtime->bs412.gain;
|
temp_result.bs412_gain = runtime->bs412.gain;
|
||||||
mpx = tanhf(mpx);
|
|
||||||
|
|
||||||
output[i] = mpx*config->master_volume; // Ensure peak deviation of 75 khz (or the set deviation), assuming we're calibrated correctly
|
output[i] = tanhf(mpx)*config->master_volume; // Ensure peak deviation of 75 khz (or the set deviation), assuming we're calibrated correctly
|
||||||
advance_oscillator(&runtime->osc);
|
|
||||||
} memcpy(result, &temp_result, sizeof(FM95_RunResult));
|
} memcpy(result, &temp_result, sizeof(FM95_RunResult));
|
||||||
_pulse_output;
|
_pulse_output;
|
||||||
}
|
}
|
||||||
@@ -432,7 +452,7 @@ void init_runtime(FM95_Runtime* runtime, const FM95_Config config) {
|
|||||||
init_oscillator(&runtime->osc, (config.calibration == 2) ? 60 : ((config.calibration == 1) ? 400 : 19000), config.sample_rate);
|
init_oscillator(&runtime->osc, (config.calibration == 2) ? 60 : ((config.calibration == 1) ? 400 : 19000), config.sample_rate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else init_oscillator(&runtime->osc, 4750, config.sample_rate);
|
else init_oscillator(&runtime->osc, 1187.5f, config.sample_rate);
|
||||||
|
|
||||||
if(config.lpf_cutoff != 0) {
|
if(config.lpf_cutoff != 0) {
|
||||||
runtime->lpf_l = iirfilt_rrrf_create_prototype(LIQUID_IIRDES_CHEBY2, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, config.lpf_order, (config.lpf_cutoff/config.sample_rate), 0.0f, 1.0f, 40.0f);
|
runtime->lpf_l = iirfilt_rrrf_create_prototype(LIQUID_IIRDES_CHEBY2, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, config.lpf_order, (config.lpf_cutoff/config.sample_rate), 0.0f, 1.0f, 40.0f);
|
||||||
@@ -451,7 +471,7 @@ void init_runtime(FM95_Runtime* runtime, const FM95_Config config) {
|
|||||||
if(runtime->bs412.init == true && (runtime->bs412.sample_rate == config.sample_rate)) {
|
if(runtime->bs412.init == true && (runtime->bs412.sample_rate == config.sample_rate)) {
|
||||||
reinit_bs412(&runtime->bs412, config.mpx_deviation, config.mpx_power, config.bs412_attack, config.bs412_release, config.bs412_max, config.bs412_gate, config.bs412_knee, config.bs412_strenght);
|
reinit_bs412(&runtime->bs412, config.mpx_deviation, config.mpx_power, config.bs412_attack, config.bs412_release, config.bs412_max, config.bs412_gate, config.bs412_knee, config.bs412_strenght);
|
||||||
} else init_bs412(&runtime->bs412, config.mpx_deviation, config.mpx_power, config.bs412_attack, config.bs412_release, config.bs412_max, config.bs412_gate, config.bs412_knee, config.bs412_strenght, config.sample_rate);
|
} else init_bs412(&runtime->bs412, config.mpx_deviation, config.mpx_power, config.bs412_attack, config.bs412_release, config.bs412_max, config.bs412_gate, config.bs412_knee, config.bs412_strenght, config.sample_rate);
|
||||||
init_stereo_encoder(&runtime->stencode, config.stereo_ssb, 4.0f, &runtime->osc, config.volumes.audio, config.volumes.pilot);
|
init_stereo_encoder(&runtime->stencode, config.stereo_ssb, 16.0f, &runtime->osc, config.volumes.audio, config.volumes.pilot);
|
||||||
|
|
||||||
float last_gain = 0.0f;
|
float last_gain = 0.0f;
|
||||||
if(config.agc_max != 0.0) {
|
if(config.agc_max != 0.0) {
|
||||||
@@ -460,6 +480,11 @@ void init_runtime(FM95_Runtime* runtime, const FM95_Config config) {
|
|||||||
initAGC(&runtime->agc, config.sample_rate, config.agc_target, config.agc_min, config.agc_max, config.agc_attack, config.agc_release);
|
initAGC(&runtime->agc, config.sample_rate, config.agc_target, config.agc_min, config.agc_max, config.agc_attack, config.agc_release);
|
||||||
runtime->agc.currentGain = last_gain;
|
runtime->agc.currentGain = last_gain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bit_ring_init(&runtime->rds_bitring[0], 2048);
|
||||||
|
runtime->rds_phase[0] = 0.0;
|
||||||
|
runtime->rds_symbol[0] = -1.0f;
|
||||||
|
runtime->rds_last_bit[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BUF_SIZE 256
|
#define BUF_SIZE 256
|
||||||
@@ -577,6 +602,17 @@ static void *handle_client(ipc_client_arg_t *arg) {
|
|||||||
data->runtime->bs412.strenght = val;
|
data->runtime->bs412.strenght = val;
|
||||||
reply = 0;
|
reply = 0;
|
||||||
break;
|
break;
|
||||||
|
case 112: {
|
||||||
|
uint8_t unpacked[8 * (BUF_SIZE - 1)];
|
||||||
|
size_t nbits = 0;
|
||||||
|
for (ssize_t i = 1; i < n; i++) {
|
||||||
|
for (int b = 7; b >= 0; b--)
|
||||||
|
unpacked[nbits++] = (buf[i] >> b) & 1;
|
||||||
|
}
|
||||||
|
bit_ring_write(&data->runtime->rds_bitring[0], unpacked, nbits);
|
||||||
|
reply = 0xff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0xfe:
|
case 0xfe:
|
||||||
// Fetch config
|
// Fetch config
|
||||||
send(fd, data->config, sizeof(FM95_Config), 0);
|
send(fd, data->config, sizeof(FM95_Config), 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user