mirror of
https://github.com/radio95-rnt/fm95.git
synced 2026-07-29 15:59: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 <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);
|
||||
if (fd < 0) { perror("ipc: socket"); return -1; }
|
||||
|
||||
@@ -63,9 +62,7 @@ static void *listener_thread(void *arg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int create_ipc(ipc_ctx_t *ctx, ipc_client_fn handler,
|
||||
const char *socket_path, void *user_data)
|
||||
{
|
||||
int create_ipc(ipc_ctx_t *ctx, ipc_client_fn handler, const char *socket_path, void *user_data) {
|
||||
ctx->handler = handler;
|
||||
ctx->user_data = user_data;
|
||||
ctx->running = 1;
|
||||
|
||||
+5
-2
@@ -30,7 +30,10 @@ float get_oscillator_cos_multiplier_ni(Oscillator *osc, float multiplier) {
|
||||
return cosf(osc->phase * multiplier);
|
||||
}
|
||||
|
||||
inline void advance_oscillator(Oscillator *osc) {
|
||||
inline bool advance_oscillator(Oscillator *osc) {
|
||||
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
|
||||
|
||||
#include "constants.h"
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
typedef struct {
|
||||
@@ -15,4 +16,4 @@ float get_oscillator_sin_sample(Oscillator *osc);
|
||||
float get_oscillator_cos_sample(Oscillator *osc);
|
||||
float get_oscillator_sin_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 "bs412.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
|
||||
|
||||
@@ -83,6 +84,10 @@ typedef struct {
|
||||
StereoEncoder stencode;
|
||||
AGC agc;
|
||||
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;
|
||||
|
||||
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);
|
||||
runtime->lpf_l = runtime->lpf_r = NULL;
|
||||
} exit_stereo_encoder(&runtime->stencode);
|
||||
|
||||
free(runtime->rds_bitring[0].bits);
|
||||
}
|
||||
|
||||
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++) {
|
||||
bool cycle = advance_oscillator(&runtime->osc);
|
||||
bool do_result = (i == BUFFER_SIZE - 1);
|
||||
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
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
|
||||
advance_oscillator(&runtime->osc);
|
||||
output[i] = tanhf(mpx)*config->master_volume; // Ensure peak deviation of 75 khz (or the set deviation), assuming we're calibrated correctly
|
||||
} memcpy(result, &temp_result, sizeof(FM95_RunResult));
|
||||
_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);
|
||||
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) {
|
||||
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)) {
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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
|
||||
@@ -577,6 +602,17 @@ static void *handle_client(ipc_client_arg_t *arg) {
|
||||
data->runtime->bs412.strenght = val;
|
||||
reply = 0;
|
||||
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:
|
||||
// Fetch config
|
||||
send(fd, data->config, sizeof(FM95_Config), 0);
|
||||
|
||||
Reference in New Issue
Block a user