kopia lustrzana https://github.com/F5OEO/PiFmRds
Functional version, with adapted levels
rodzic
23d0c15396
commit
93aed2e712
|
@ -23,4 +23,4 @@ waveforms.h: generate_waveforms.py
|
|||
python generate_waveforms.py
|
||||
|
||||
clean:
|
||||
rm *.o
|
||||
rm *.o
|
||||
|
|
148
src/pi_fm_rds.c
148
src/pi_fm_rds.c
|
@ -101,7 +101,7 @@
|
|||
#include "rds.h"
|
||||
|
||||
|
||||
#define NUM_SAMPLES 400000
|
||||
#define NUM_SAMPLES 50000
|
||||
#define NUM_CBS (NUM_SAMPLES * 2)
|
||||
|
||||
#define BCM2708_DMA_NO_WIDE_BURSTS (1<<26)
|
||||
|
@ -125,10 +125,6 @@
|
|||
#define GPIO_BASE 0x20200000
|
||||
#define GPIO_LEN 0xB4
|
||||
|
||||
// see http://elinux.org/BCM2835_registers#PM
|
||||
#define PM_BASE 0x20100000
|
||||
#define PM_PADS2 0x2C
|
||||
#define PM_LEN 0x114
|
||||
|
||||
#define PWM_CTL (0x00/4)
|
||||
#define PWM_DMAC (0x08/4)
|
||||
|
@ -154,7 +150,7 @@
|
|||
#define GPFSEL0 (0x00/4)
|
||||
|
||||
#define PLLFREQ 500000000. // PLLD is running at 500MHz ////
|
||||
#define CARRIERFREQ 107900000. // Carrier frequency is 100MHz
|
||||
|
||||
// The deviation specifies how wide the signal is. Use 25.0 for WBFM
|
||||
// (broadcast radio) and about 3.5 for NBFM (walkie-talkie style radio)
|
||||
#define DEVIATION 25.0
|
||||
|
@ -178,7 +174,6 @@ static volatile uint32_t *pwm_reg;
|
|||
static volatile uint32_t *clk_reg;
|
||||
static volatile uint32_t *dma_reg;
|
||||
static volatile uint32_t *gpio_reg;
|
||||
static volatile uint32_t *pm_reg;
|
||||
|
||||
struct control_data_s {
|
||||
dma_cb_t cb[NUM_CBS];
|
||||
|
@ -269,7 +264,7 @@ map_peripheral(uint32_t base, uint32_t len)
|
|||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i, fd, pid, freq_ctl;
|
||||
int i, fd, pid;
|
||||
char pagemap_fn[64];
|
||||
|
||||
// Catch all signals possible - it is vital we kill the DMA engine
|
||||
|
@ -281,16 +276,11 @@ main(int argc, char **argv)
|
|||
sa.sa_handler = terminate;
|
||||
sigaction(i, &sa, NULL);
|
||||
}
|
||||
|
||||
// Calculate the frequency control word
|
||||
// The fractional part is stored in the lower 12 bits
|
||||
freq_ctl = ((float)(PLLFREQ / CARRIERFREQ)) * ( 1 << 12 );
|
||||
|
||||
dma_reg = map_peripheral(DMA_BASE, DMA_LEN);
|
||||
pwm_reg = map_peripheral(PWM_BASE, PWM_LEN);
|
||||
clk_reg = map_peripheral(CLK_BASE, CLK_LEN);
|
||||
gpio_reg = map_peripheral(GPIO_BASE, GPIO_LEN);
|
||||
pm_reg = map_peripheral(PM_BASE, PM_LEN);
|
||||
|
||||
virtbase = mmap(NULL, NUM_PAGES * PAGE_SIZE, PROT_READ|PROT_WRITE,
|
||||
MAP_SHARED|MAP_ANONYMOUS|MAP_NORESERVE|MAP_LOCKED,
|
||||
|
@ -337,6 +327,14 @@ main(int argc, char **argv)
|
|||
uint32_t phys_sample_dst = 0x7e101074;
|
||||
uint32_t phys_pwm_fifo_addr = 0x7e20c000 + 0x18;
|
||||
|
||||
|
||||
uint32_t carrier_freq = 107900000;
|
||||
|
||||
// Calculate the frequency control word
|
||||
// The fractional part is stored in the lower 12 bits
|
||||
uint32_t freq_ctl = ((float)(PLLFREQ / carrier_freq)) * ( 1 << 12 );
|
||||
|
||||
|
||||
for (i = 0; i < NUM_SAMPLES; i++) {
|
||||
ctl->sample[i] = 0x5a << 24 | freq_ctl; // Silence
|
||||
// Write a frequency sample
|
||||
|
@ -390,10 +388,6 @@ main(int argc, char **argv)
|
|||
pwm_reg[PWM_CTL] = PWMCTL_USEF1 | PWMCTL_PWEN1;
|
||||
udelay(10);
|
||||
|
||||
// set output drive strength to 16 mA
|
||||
// see http://fr.scribd.com/doc/101830961/GPIO-Pads-Control2
|
||||
pm_reg[PM_PADS2] = 0x5A000000 | 7;
|
||||
|
||||
|
||||
// Initialise the DMA
|
||||
dma_reg[DMA_CS] = BCM2708_DMA_RESET;
|
||||
|
@ -403,36 +397,57 @@ main(int argc, char **argv)
|
|||
dma_reg[DMA_DEBUG] = 7; // clear debug error flags
|
||||
dma_reg[DMA_CS] = 0x10880001; // go, mid priority, wait for outstanding writes
|
||||
|
||||
// Nearly there.. open the .wav file specified on the cmdline
|
||||
|
||||
// Try to read audio samples from a .wav file
|
||||
fd = 0;
|
||||
|
||||
short data[1024];
|
||||
int data_len = 0;
|
||||
|
||||
if (argc > 1) {
|
||||
fd = open(argv[1], 'r');
|
||||
fd = open(argv[1], 'r');
|
||||
|
||||
if (fd < 0)
|
||||
fatal("Failed to open .wav file\n");
|
||||
data_len = read(fd, data, 22);
|
||||
if (data_len < 22)
|
||||
fatal("Failed to read .wav file\n");
|
||||
data_len = 0;
|
||||
}
|
||||
|
||||
short data[1024];
|
||||
int data_len = read(fd, data, sizeof(data));
|
||||
if (data_len < 0)
|
||||
fatal("Failed to read .wav file\n");
|
||||
data_len /= 2;
|
||||
if (data_len < 23)
|
||||
fatal("Initial read of .wav file too short\n");
|
||||
|
||||
|
||||
uint32_t last_cb = (uint32_t)ctl->cb;
|
||||
int data_index = 22;
|
||||
int data_index = 0;
|
||||
|
||||
float rds_data[RDS_DATA_SIZE];
|
||||
int rds_index = sizeof(rds_data);
|
||||
|
||||
set_rds_params(0x2345, "Hello");
|
||||
char ps[9] = {0};
|
||||
set_rds_pi(0x2345);
|
||||
set_rds_rt("RPi-Live - Live RDS transmission from the Raspberry Pi!");
|
||||
uint16_t count = 0;
|
||||
uint16_t count2 = 0;
|
||||
|
||||
printf("Starting to transmit\n");
|
||||
|
||||
int do_tune = 1;
|
||||
int tune_on = 1;
|
||||
uint8_t tune_idx = 0;
|
||||
uint32_t tune_cycle_counter = 0;
|
||||
float tune_level = 1;
|
||||
|
||||
for (;;) {
|
||||
usleep(1000);
|
||||
if(count == 512) {
|
||||
snprintf(ps, 9, "%08d", count2);
|
||||
set_rds_ps(ps);
|
||||
count2++;
|
||||
}
|
||||
if(count == 1024) {
|
||||
set_rds_ps("RPi-Live");
|
||||
count = 0;
|
||||
}
|
||||
count++;
|
||||
|
||||
|
||||
usleep(5000);
|
||||
|
||||
uint32_t cur_cb = mem_phys_to_virt(dma_reg[DMA_CONBLK_AD]);
|
||||
int last_sample = (last_cb - (uint32_t)virtbase) / (sizeof(dma_cb_t) * 2);
|
||||
|
@ -443,38 +458,61 @@ main(int argc, char **argv)
|
|||
free_slots += NUM_SAMPLES;
|
||||
|
||||
while (free_slots >= SUBSIZE) {
|
||||
// generate RDS samples if necessary
|
||||
if(rds_index >= RDS_DATA_SIZE) {
|
||||
get_rds_samples(rds_data, RDS_DATA_SIZE);
|
||||
rds_index = 0;
|
||||
}
|
||||
|
||||
//float dval = (float)(data[data_index])/65536.0 * DEVIATION;
|
||||
float dval = rds_data[rds_index] / 3. * DEVIATION;
|
||||
rds_index++;
|
||||
|
||||
int intval = (int)((floor)(dval));
|
||||
int frac = (int)((dval - (float)intval) * SUBSIZE);
|
||||
int j;
|
||||
|
||||
// I'm sure this code could do a better job of subsampling, either by
|
||||
// distributing the '+1's evenly across the 10 subsamples, or maybe
|
||||
// by taking the previous and next samples in to account too.
|
||||
for (j = 0; j < SUBSIZE; j++) {
|
||||
ctl->sample[last_sample++] = (0x5A << 24 | freq_ctl) + (frac > j ? intval + 1 : intval);
|
||||
if (last_sample == NUM_SAMPLES)
|
||||
last_sample = 0;
|
||||
}
|
||||
free_slots -= SUBSIZE;
|
||||
if (++data_index >= data_len) {
|
||||
data_len = read(fd, data, sizeof(data));
|
||||
data_index = 0;
|
||||
// read samples in the wav file if necessary
|
||||
if(fd && data_len == 0) {
|
||||
data_len = read(fd, data, sizeof(data));
|
||||
if (data_len < 0)
|
||||
fatal("Error reading data: %m\n");
|
||||
// Should really wait for outstanding samples to be processed here..
|
||||
data_len /= 2;
|
||||
if (data_len == 0)
|
||||
terminate(0);
|
||||
if(data_len == 0) {
|
||||
lseek(fd, 22, SEEK_SET);
|
||||
}
|
||||
data_index = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//float dval = (float)(data[data_index])/65536.0 * DEVIATION;
|
||||
float dval = rds_data[rds_index] * (DEVIATION / 10.);
|
||||
rds_index++;
|
||||
|
||||
// add modulation from a 445 Hz (228000 /2 /256) tune
|
||||
if(do_tune) {
|
||||
if(tune_idx == 0 && tune_on) {
|
||||
tune_level = 1-tune_level;
|
||||
}
|
||||
dval += tune_level * DEVIATION/5.;
|
||||
tune_idx++;
|
||||
|
||||
tune_cycle_counter++;
|
||||
if(tune_cycle_counter >= 228000) {
|
||||
tune_cycle_counter = 0;
|
||||
tune_on = !tune_on;
|
||||
}
|
||||
}
|
||||
// add modulation from .wav?
|
||||
else if(fd && data_len > 0) {
|
||||
data_index++;
|
||||
data_len--;
|
||||
// do something here
|
||||
}
|
||||
|
||||
int intval = (int)((floor)(dval));
|
||||
//int frac = (int)((dval - (float)intval) * SUBSIZE);
|
||||
//int j;
|
||||
|
||||
|
||||
ctl->sample[last_sample++] = (0x5A << 24 | freq_ctl) + intval; //(frac > j ? intval + 1 : intval);
|
||||
if (last_sample == NUM_SAMPLES)
|
||||
last_sample = 0;
|
||||
|
||||
free_slots -= SUBSIZE;
|
||||
}
|
||||
last_cb = (uint32_t)virtbase + last_sample * sizeof(dma_cb_t) * 2;
|
||||
}
|
||||
|
|
46
src/rds.c
46
src/rds.c
|
@ -23,11 +23,13 @@
|
|||
#include <stdio.h>
|
||||
#include "waveforms.h"
|
||||
|
||||
#define MAX_TEXT_LENGTH 64
|
||||
#define RT_LENGTH 64
|
||||
#define PS_LENGTH 8
|
||||
#define GROUP_LENGTH 4
|
||||
|
||||
struct {
|
||||
char text[MAX_TEXT_LENGTH];
|
||||
char ps[PS_LENGTH];
|
||||
char rt[RT_LENGTH];
|
||||
uint16_t pi;
|
||||
} rds_params;
|
||||
|
||||
|
@ -42,7 +44,7 @@ struct {
|
|||
#define BITS_PER_GROUP (GROUP_LENGTH * (BLOCK_SIZE+POLY_DEG))
|
||||
#define SAMPLES_PER_BIT 192
|
||||
#define FILTER_SIZE (sizeof(waveform_biphase)/sizeof(float))
|
||||
#define SAMPLE_BUFFER_SIZE (8*SAMPLES_PER_BIT)
|
||||
#define SAMPLE_BUFFER_SIZE (SAMPLES_PER_BIT + FILTER_SIZE)
|
||||
|
||||
|
||||
uint16_t offset_words[] = {0x0FC, 0x198, 0x168, 0x1B4};
|
||||
|
@ -82,13 +84,13 @@ void get_rds_group(int *buffer) {
|
|||
if(state < 4) {
|
||||
blocks[1] = 0x0000 | ps_state;
|
||||
blocks[2] = 0xCDCD; // no AF
|
||||
blocks[3] = rds_params.text[ps_state*2]<<8 | rds_params.text[ps_state*2+1];
|
||||
blocks[3] = rds_params.ps[ps_state*2]<<8 | rds_params.ps[ps_state*2+1];
|
||||
ps_state++;
|
||||
if(ps_state >= 4) ps_state = 0;
|
||||
} else { // state == 5
|
||||
blocks[1] = 0x2000 | rt_state;
|
||||
blocks[2] = rds_params.text[rt_state*4+0]<<8 | rds_params.text[rt_state*4+1];
|
||||
blocks[3] = rds_params.text[rt_state*4+2]<<8 | rds_params.text[rt_state*4+3];
|
||||
blocks[2] = rds_params.rt[rt_state*4+0]<<8 | rds_params.rt[rt_state*4+1];
|
||||
blocks[3] = rds_params.rt[rt_state*4+2]<<8 | rds_params.rt[rt_state*4+3];
|
||||
rt_state++;
|
||||
if(rt_state >= 16) rt_state = 0;
|
||||
}
|
||||
|
@ -145,38 +147,23 @@ void get_rds_samples(float *buffer, int count) {
|
|||
|
||||
inverting = (cur_output == 1);
|
||||
|
||||
/*
|
||||
// zero out last bit
|
||||
int idx = in_sample_index-1;
|
||||
printf(" Wipe: %d -> ", idx);
|
||||
|
||||
for(int j=0; j<SAMPLES_PER_BIT; j++) {
|
||||
if(idx<0) idx = SAMPLE_BUFFER_SIZE-1;
|
||||
sample_buffer[idx] = 0;
|
||||
idx--;
|
||||
}
|
||||
printf("%d\n", idx+1);
|
||||
*/
|
||||
|
||||
float *src = waveform_biphase;
|
||||
int idx = in_sample_index;
|
||||
//printf("In: %d -> ", idx);
|
||||
|
||||
for(int j=0; j<FILTER_SIZE; j++) {
|
||||
float val = (*src++);
|
||||
if(inverting) val = -val;
|
||||
sample_buffer[idx++] += val;
|
||||
if(idx >= SAMPLE_BUFFER_SIZE) idx = 0;
|
||||
}
|
||||
//printf("%d\n", idx-1);
|
||||
|
||||
in_sample_index += SAMPLES_PER_BIT;
|
||||
if(in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE;
|
||||
|
||||
bit_pos++;
|
||||
sample_count = 0;
|
||||
//printf("%d", cur_bit); fflush(stdout);
|
||||
}
|
||||
|
||||
//printf("[%d]", out_sample_index);
|
||||
float sample = sample_buffer[out_sample_index];
|
||||
sample_buffer[out_sample_index] = 0;
|
||||
out_sample_index++;
|
||||
|
@ -199,8 +186,15 @@ void get_rds_samples(float *buffer, int count) {
|
|||
}
|
||||
}
|
||||
|
||||
void set_rds_params(uint16_t pi_code, char *text) {
|
||||
void set_rds_pi(uint16_t pi_code) {
|
||||
rds_params.pi = pi_code;
|
||||
|
||||
strncpy(rds_params.text, text, 64);
|
||||
|
||||
}
|
||||
|
||||
void set_rds_rt(char *rt) {
|
||||
strncpy(rds_params.rt, rt, 64);
|
||||
}
|
||||
|
||||
void set_rds_ps(char *ps) {
|
||||
strncpy(rds_params.ps, ps, 8);
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
#include <stdint.h>
|
||||
|
||||
extern void get_rds_samples(float *buffer, int count);
|
||||
extern void set_rds_params(uint16_t pi_code, char *text);
|
||||
extern void set_rds_pi(uint16_t pi_code);
|
||||
extern void set_rds_rt(char *rt);
|
||||
extern void set_rds_ps(char *ps);
|
||||
|
|
Ładowanie…
Reference in New Issue