Variant for superhet + amplifier

This commit is contained in:
Jan Hamal Dvořák 2024-08-05 20:11:50 +02:00
parent 1fe4633601
commit 36ca06f31b
3 changed files with 85 additions and 347 deletions

View file

@ -37,7 +37,7 @@ blocks:
id: variable
parameters:
comment: ''
value: '88_200_000'
value: '94_600_000'
states:
bus_sink: false
bus_source: false
@ -70,7 +70,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [640, 336.0]
coordinate: [832, 336.0]
rotation: 0
state: true
- name: analog_wfm_rcv_pll_0
@ -88,7 +88,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [640, 440.0]
coordinate: [832, 440.0]
rotation: 0
state: true
- name: audio_sink_0
@ -105,7 +105,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [928, 448.0]
coordinate: [1120, 448.0]
rotation: 0
state: true
- name: blocks_message_debug_0
@ -120,7 +120,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [928, 32.0]
coordinate: [1120, 32.0]
rotation: 0
state: true
- name: blocks_probe_rate_0
@ -140,7 +140,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [640, 40.0]
coordinate: [832, 40.0]
rotation: 0
state: true
- name: low_pass_filter_0
@ -164,7 +164,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [928, 284.0]
coordinate: [1120, 284.0]
rotation: 0
state: enabled
- name: osmosdr_source_0
@ -374,7 +374,7 @@ blocks:
freq7: 100e6
freq8: 100e6
freq9: 100e6
gain0: '30'
gain0: '0'
gain1: '10'
gain10: '10'
gain11: '10'
@ -618,7 +618,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [640, 232.0]
coordinate: [832, 232.0]
rotation: 0
state: true
- name: qtgui_time_sink_x_0_0
@ -676,7 +676,7 @@ blocks:
marker9: '-1'
name: '"FM Demodulation"'
nconnections: '1'
size: '512'
size: '128'
srate: samp_rate // (2 ** 3)
stemplot: 'False'
style1: '1'
@ -715,7 +715,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [1128, 312.0]
coordinate: [1320, 312.0]
rotation: 0
state: true
- name: qtgui_waterfall_sink_x_0_0
@ -776,7 +776,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [640, 128.0]
coordinate: [832, 128.0]
rotation: 0
state: true
- name: qtgui_waterfall_sink_x_0_0_0_0
@ -837,7 +837,7 @@ blocks:
bus_sink: false
bus_source: false
bus_structure: null
coordinate: [928, 184.0]
coordinate: [1120, 184.0]
rotation: 0
state: true

View file

@ -17,6 +17,7 @@ target_link_libraries(
pico_stdlib
pico_util
hardware_divider
hardware_adc
hardware_dma
hardware_pio
hardware_pwm

View file

@ -4,6 +4,7 @@
#include <pico/util/queue.h>
#include <hardware/clocks.h>
#include <hardware/adc.h>
#include <hardware/dma.h>
#include <hardware/gpio.h>
#include <hardware/pll.h>
@ -28,20 +29,19 @@
#define INIT_FREQ 94600000
#define INIT_GAIN 127
#define LO_PIN 9
#define RX_PIN 13
#define FB_PIN 5
#define LO_PIN 21
#define RX_PIN 26
#define PSU_PIN 23
#define PIO pio1
#define SM_LO 0
#define SM_FB 1
#define SM_RX 2
#define SM_AD 3
#define IQ_SAMPLES 32
#define IQ_BLOCK_LEN (2 * IQ_SAMPLES)
#define IQ_QUEUE_LEN 8
#define IQ_QUEUE_LEN 4
#define ADC_RATE (2 * MHZ)
#define DECIMATE 8
/*
* NOTE: Must have 256 phases with 256 bytes each.
@ -61,39 +61,19 @@ static uint32_t lo_phase[LO_NUM_PHASES][LO_PHASE_WORDS]
static uint32_t nco_addr = (uint32_t)lo_phase;
#define DECIMATE 16
#define RX_BITS_DEPTH 8
#define RX_WORDS (1 << (RX_BITS_DEPTH - 2))
#define RX_STRIDE (2 * DECIMATE)
static_assert(RX_WORDS >= 2 * RX_STRIDE, "RX_WORDS >= 2 * RX_STRIDE");
static uint32_t rx_cos[RX_WORDS] __attribute__((__aligned__(1 << RX_BITS_DEPTH)));
#define NUM_GAINS 29
static int gains[NUM_GAINS] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157,
166, 197, 207, 229, 254, 280, 297, 328, 338, 364,
372, 386, 402, 421, 434, 439, 445, 480, 496 };
static int sample_rate = INIT_SAMPLE_RATE;
static int max_amplitude = CLK_SYS_HZ / INIT_SAMPLE_RATE / 2;
static int max_amplitude_mul = 65536 / (CLK_SYS_HZ / INIT_SAMPLE_RATE / 2);
static int gain = INIT_GAIN;
static int frequency = INIT_FREQ;
static int dma_ch_rx1 = -1;
static int dma_ch_rx2 = -1;
static int dma_ch_nco1 = -1;
static int dma_ch_nco2 = -1;
static int dma_ch_nco3 = -1;
static int dma_ch_mix = -1;
static int dma_ch_samp_cos = -1;
static int dma_t_samp = -1;
static int dma_ch_in_cos = -1;
static queue_t iq_queue;
static uint8_t iq_queue_buffer[IQ_QUEUE_LEN][IQ_BLOCK_LEN];
static size_t iq_queue_pos = 0;
@ -101,9 +81,6 @@ static size_t iq_queue_pos = 0;
static uint32_t rnd = 0;
static int origin_lo = -1;
static int origin_rx = -1;
static int origin_fb = -1;
static int origin_ad = 0;
inline static __unused uint32_t rnd_next()
{
@ -124,8 +101,8 @@ static void init_lo()
gpio_disable_pulls(LO_PIN);
pio_gpio_init(PIO, LO_PIN);
gpio_set_drive_strength(LO_PIN, GPIO_DRIVE_STRENGTH_12MA);
gpio_set_slew_rate(LO_PIN, GPIO_SLEW_RATE_FAST);
gpio_set_drive_strength(LO_PIN, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_slew_rate(LO_PIN, GPIO_SLEW_RATE_SLOW);
const uint16_t insn[] = {
pio_encode_out(pio_pindirs, 1),
@ -156,136 +133,6 @@ static void init_lo()
pio_sm_exec_wait_blocking(PIO, SM_LO, pio_encode_set(pio_pins, 0));
}
static void init_fb()
{
gpio_disable_pulls(FB_PIN);
pio_gpio_init(PIO, FB_PIN);
// NOTE: Not sure if this is ideal.
hw_set_bits(&PIO->input_sync_bypass, 1u << RX_PIN);
gpio_set_input_hysteresis_enabled(RX_PIN, false);
gpio_set_drive_strength(FB_PIN, GPIO_DRIVE_STRENGTH_2MA);
gpio_set_slew_rate(FB_PIN, GPIO_SLEW_RATE_SLOW);
const uint16_t insn[] = {
pio_encode_mov_not(pio_pins, pio_pins) | pio_encode_sideset(1, 1) |
pio_encode_delay(0),
//pio_encode_nop() | pio_encode_sideset(1, 0) | pio_encode_delay(0),
};
pio_program_t prog = {
.instructions = insn,
.length = sizeof(insn) / sizeof(*insn),
.origin = origin_fb,
};
pio_sm_restart(PIO, SM_FB);
pio_sm_clear_fifos(PIO, SM_FB);
if (pio_can_add_program(PIO, &prog))
origin_fb = pio_add_program(PIO, &prog);
pio_sm_config pc = pio_get_default_sm_config();
sm_config_set_sideset(&pc, 1, false, true);
sm_config_set_in_pins(&pc, RX_PIN);
sm_config_set_out_pins(&pc, FB_PIN, 1);
sm_config_set_set_pins(&pc, FB_PIN, 1);
sm_config_set_sideset_pins(&pc, FB_PIN);
sm_config_set_wrap(&pc, origin_fb, origin_fb + prog.length - 1);
sm_config_set_clkdiv_int_frac(&pc, 1, 0);
pio_sm_init(PIO, SM_FB, origin_fb, &pc);
pio_sm_set_consecutive_pindirs(PIO, SM_FB, FB_PIN, 1, GPIO_OUT);
}
static void init_rx()
{
gpio_disable_pulls(RX_PIN);
pio_gpio_init(PIO, RX_PIN);
const uint16_t insn[] = {
pio_encode_in(pio_pins, 1) | pio_encode_delay(0),
};
pio_program_t prog = {
.instructions = insn,
.length = sizeof(insn) / sizeof(*insn),
.origin = origin_rx,
};
pio_sm_restart(PIO, SM_RX);
pio_sm_clear_fifos(PIO, SM_RX);
if (pio_can_add_program(PIO, &prog))
origin_rx = pio_add_program(PIO, &prog);
pio_sm_config pc = pio_get_default_sm_config();
sm_config_set_in_pins(&pc, RX_PIN);
sm_config_set_wrap(&pc, origin_rx, origin_rx + prog.length - 1);
sm_config_set_clkdiv_int_frac(&pc, 1, 0);
sm_config_set_fifo_join(&pc, PIO_FIFO_JOIN_RX);
sm_config_set_in_shift(&pc, false, true, 32);
pio_sm_init(PIO, SM_RX, origin_rx, &pc);
pio_sm_set_consecutive_pindirs(PIO, SM_RX, RX_PIN, 1, GPIO_IN);
}
static const uint32_t samp_insn = 16;
static void init_ad()
{
const uint16_t insn[] = {
pio_encode_out(pio_pc, 4), // 0000 +0
pio_encode_jmp_x_dec(0), // 0001 +1
pio_encode_jmp_x_dec(0), // 0010 +1
pio_encode_jmp_y_dec(0), // 0011 +2
pio_encode_jmp_x_dec(0), // 0100 +1
pio_encode_jmp_y_dec(0), // 0101 +2
pio_encode_jmp_y_dec(0), // 0110 +2
pio_encode_jmp_y_dec(1), // 0111 +2 +1
pio_encode_jmp_x_dec(0), // 1000 +1
pio_encode_jmp_y_dec(0), // 1001 +2
pio_encode_jmp_y_dec(0), // 1010 +2
pio_encode_jmp_y_dec(1), // 1011 +2 +1
pio_encode_jmp_y_dec(0), // 1100 +2
pio_encode_jmp_y_dec(1), // 1101 +2 +1
pio_encode_jmp_y_dec(1), // 1110 +2 +1
pio_encode_jmp_y_dec(3), // 1111 +2 +2
/*
* Should wrap here.
* Jump to this portion must be inserted from the outside.
*/
pio_encode_in(pio_y, 32),
pio_encode_in(pio_x, 32),
pio_encode_set(pio_y, 0),
pio_encode_set(pio_x, 0),
pio_encode_jmp_y_dec(21),
pio_encode_jmp_x_dec(22),
pio_encode_out(pio_pc, 4),
};
pio_program_t prog = {
.instructions = insn,
.length = sizeof(insn) / sizeof(*insn),
.origin = origin_ad,
};
pio_sm_restart(PIO, SM_AD);
pio_sm_clear_fifos(PIO, SM_AD);
if (pio_can_add_program(PIO, &prog))
pio_add_program(PIO, &prog);
pio_sm_config pc = pio_get_default_sm_config();
sm_config_set_wrap(&pc, origin_ad, origin_ad + 15);
sm_config_set_clkdiv_int_frac(&pc, 1, 0);
sm_config_set_in_shift(&pc, false, true, 32);
sm_config_set_out_shift(&pc, false, true, 32);
pio_sm_init(PIO, SM_AD, origin_ad, &pc);
}
static void lo_generate_phase(uint32_t *buf, size_t len, uint32_t step, uint32_t phase)
{
for (size_t i = 0; i < len; i++) {
@ -313,37 +160,13 @@ static void rx_lo_init(double freq)
static void rf_rx_start()
{
dma_ch_rx1 = dma_claim_unused_channel(true);
dma_ch_rx2 = dma_claim_unused_channel(true);
dma_ch_nco1 = dma_claim_unused_channel(true);
dma_ch_nco2 = dma_claim_unused_channel(true);
dma_ch_nco3 = dma_claim_unused_channel(true);
dma_ch_mix = dma_claim_unused_channel(true);
dma_ch_samp_cos = dma_claim_unused_channel(true);
dma_channel_config dma_conf;
/* Copy PDM bitstream into decimator. */
dma_conf = dma_channel_get_default_config(dma_ch_rx1);
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
channel_config_set_read_increment(&dma_conf, false);
channel_config_set_write_increment(&dma_conf, false);
channel_config_set_dreq(&dma_conf, pio_get_dreq(PIO, SM_RX, GPIO_IN));
channel_config_set_chain_to(&dma_conf, dma_ch_rx2);
dma_channel_configure(dma_ch_rx1, &dma_conf, &PIO->txf[SM_AD], &PIO->rxf[SM_RX], UINT_MAX,
false);
dma_conf = dma_channel_get_default_config(dma_ch_rx2);
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
channel_config_set_read_increment(&dma_conf, false);
channel_config_set_write_increment(&dma_conf, false);
channel_config_set_dreq(&dma_conf, pio_get_dreq(PIO, SM_RX, GPIO_IN));
channel_config_set_chain_to(&dma_conf, dma_ch_rx1);
dma_channel_configure(dma_ch_rx2, &dma_conf, &PIO->txf[SM_AD], &PIO->rxf[SM_RX], UINT_MAX,
false);
/* Step the NCO. */
dma_conf = dma_channel_get_default_config(dma_ch_nco1);
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
@ -382,191 +205,105 @@ static void rf_rx_start()
dma_channel_configure(dma_ch_mix, &dma_conf, &PIO->txf[SM_LO], lo_phase, LO_PHASE_WORDS,
false);
/* Trigger accumulator values push. */
dma_conf = dma_channel_get_default_config(dma_ch_samp_cos);
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
channel_config_set_read_increment(&dma_conf, false);
channel_config_set_write_increment(&dma_conf, false);
channel_config_set_high_priority(&dma_conf, true);
channel_config_set_dreq(&dma_conf, dma_get_timer_dreq(dma_t_samp));
dma_channel_configure(dma_ch_samp_cos, &dma_conf, &PIO->sm[SM_AD].instr, &samp_insn,
UINT_MAX, false);
init_ad();
init_lo();
init_fb();
init_rx();
dma_channel_start(dma_ch_rx1);
dma_channel_start(dma_ch_nco1);
dma_channel_start(dma_ch_samp_cos);
pio_set_sm_mask_enabled(PIO, 0x0f, true);
pio_sm_set_enabled(PIO, SM_LO, true);
}
static void rf_rx_stop(void)
{
pio_set_sm_mask_enabled(PIO, 0x0f, false);
pio_sm_set_enabled(PIO, SM_LO, false);
sleep_us(10);
dma_channel_clear_chain_to(dma_ch_rx1);
dma_channel_clear_chain_to(dma_ch_rx2);
dma_channel_clear_chain_to(dma_ch_nco1);
dma_channel_clear_chain_to(dma_ch_nco2);
dma_channel_clear_chain_to(dma_ch_nco3);
dma_channel_clear_chain_to(dma_ch_mix);
dma_channel_clear_chain_to(dma_ch_samp_cos);
dma_channel_abort(dma_ch_rx1);
dma_channel_abort(dma_ch_rx2);
dma_channel_abort(dma_ch_nco1);
dma_channel_abort(dma_ch_nco2);
dma_channel_abort(dma_ch_nco3);
dma_channel_abort(dma_ch_mix);
dma_channel_abort(dma_ch_samp_cos);
dma_channel_cleanup(dma_ch_rx1);
dma_channel_cleanup(dma_ch_rx2);
dma_channel_cleanup(dma_ch_nco1);
dma_channel_cleanup(dma_ch_nco2);
dma_channel_cleanup(dma_ch_nco3);
dma_channel_cleanup(dma_ch_mix);
dma_channel_cleanup(dma_ch_samp_cos);
dma_channel_unclaim(dma_ch_rx1);
dma_channel_unclaim(dma_ch_rx2);
dma_channel_unclaim(dma_ch_nco1);
dma_channel_unclaim(dma_ch_nco2);
dma_channel_unclaim(dma_ch_nco3);
dma_channel_unclaim(dma_ch_mix);
dma_channel_unclaim(dma_ch_samp_cos);
dma_ch_rx1 = -1;
dma_ch_rx2 = -1;
dma_ch_nco1 = -1;
dma_ch_nco2 = -1;
dma_ch_nco3 = -1;
dma_ch_mix = -1;
dma_ch_samp_cos = -1;
}
struct IQ {
int I, Q;
};
inline static const uint32_t *next_stride()
inline static int nextQ(void)
{
static int tail = 0;
static int dc = 0;
int head, delta;
int x = adc_fifo_get_blocking();
int Q = ((x << 16) - dc) >> 16;
dc += Q;
loop:
head = (dma_hw->ch[dma_ch_in_cos].write_addr >> 2) & (RX_WORDS - 1);
delta = head - tail;
if (delta < 0)
delta += RX_WORDS;
if (delta < RX_STRIDE)
goto loop;
const uint32_t *stride = rx_cos + tail;
tail = (tail + RX_STRIDE) & (RX_WORDS - 1);
return stride;
}
inline static int nextQ(const uint32_t **stride)
{
int x2 = *(*stride)++;
int x1 = *(*stride)++;
return x2 + x2 + x1 + max_amplitude;
return Q;
}
inline static struct IQ next_sample()
{
int I = 0, Q = 0;
const uint32_t *stride = next_stride();
int x07 = nextQ();
I += 36 * x07;
Q += 36 * x07;
int x15 = nextQ(&stride);
I += 93 * x15;
Q += 39 * x15;
int x06 = nextQ();
I += 0 * x06;
Q += 51 * x06;
int x14 = nextQ(&stride);
I += 71 * x14;
Q += 71 * x14;
int x05 = nextQ();
I += -36 * x05;
Q += 36 * x05;
int x13 = nextQ(&stride);
I += 39 * x13;
Q += 93 * x13;
int x04 = nextQ();
I += -51 * x04;
Q += 0 * x04;
int x12 = nextQ(&stride);
I += 0 * x12;
Q += 101 * x12;
int x03 = nextQ();
I += -36 * x03;
Q += -36 * x03;
int x11 = nextQ(&stride);
I += -39 * x11;
Q += 93 * x11;
int x02 = nextQ();
I += 0 * x02;
Q += -51 * x02;
int x10 = nextQ(&stride);
I += -71 * x10;
Q += 71 * x10;
int x01 = nextQ();
I += 36 * x01;
Q += -36 * x01;
int x09 = nextQ(&stride);
I += -93 * x09;
Q += 39 * x09;
int x08 = nextQ(&stride);
I += -101 * x08;
Q += 0 * x08;
int x07 = nextQ(&stride);
I += -93 * x07;
Q += -39 * x07;
int x06 = nextQ(&stride);
I += -71 * x06;
Q += -71 * x06;
int x05 = nextQ(&stride);
I += -39 * x05;
Q += -93 * x05;
int x04 = nextQ(&stride);
I += 0 * x04;
Q += -101 * x04;
int x03 = nextQ(&stride);
I += 39 * x03;
Q += -93 * x03;
int x02 = nextQ(&stride);
I += 71 * x02;
Q += -71 * x02;
int x01 = nextQ(&stride);
I += 93 * x01;
Q += -39 * x01;
int x00 = nextQ(&stride);
I += 101 * x00;
int x00 = nextQ();
I += 51 * x00;
Q += 0 * x00;
I *= gain;
I /= 1024;
I *= max_amplitude_mul;
I += 127.4 * (1 << 16);
I /= (1 << 16);
I >>= 8;
I += 127.4 * 256;
I >>= 8;
Q *= gain;
Q /= 1024;
Q *= max_amplitude_mul;
Q += 127.4 * (1 << 16);
Q /= (1 << 16);
Q >>= 8;
Q += 127.4 * 256;
Q >>= 8;
return (struct IQ){ I, Q };
}
@ -617,11 +354,11 @@ static void run_command(uint8_t cmd, uint32_t arg)
rx_lo_init(frequency + sample_rate);
} else if (0x02 == cmd) {
/* Set the rate at which IQ sample pairs are sent */
if (arg > (ADC_RATE / DECIMATE))
arg = ADC_RATE / DECIMATE;
sample_rate = arg;
max_amplitude = CLK_SYS_HZ / sample_rate / 2;
max_amplitude_mul = 65536 / max_amplitude;
dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE));
rx_lo_init(frequency + sample_rate);
adc_set_clkdiv(96.0f * ((float)ADC_RATE / (sample_rate * DECIMATE)) - 1.0f);
} else if (0x04 == cmd) {
/* Set the tuner gain level */
gain = INIT_GAIN * powf(10.0f, arg / 200.0f);
@ -664,18 +401,6 @@ static void do_rx()
rf_rx_start();
sleep_us(100);
dma_ch_in_cos = dma_claim_unused_channel(true);
dma_channel_config dma_conf;
dma_conf = dma_channel_get_default_config(dma_ch_in_cos);
channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32);
channel_config_set_read_increment(&dma_conf, false);
channel_config_set_write_increment(&dma_conf, true);
channel_config_set_ring(&dma_conf, GPIO_OUT, RX_BITS_DEPTH);
channel_config_set_dreq(&dma_conf, pio_get_dreq(PIO, SM_AD, false));
dma_channel_configure(dma_ch_in_cos, &dma_conf, rx_cos, &PIO->rxf[SM_AD], UINT_MAX, true);
multicore_launch_core1(rf_rx);
const uint8_t *block;
@ -708,18 +433,24 @@ done:
multicore_reset_core1();
rf_rx_stop();
dma_channel_clear_chain_to(dma_ch_in_cos);
dma_channel_abort(dma_ch_in_cos);
dma_channel_cleanup(dma_ch_in_cos);
dma_channel_unclaim(dma_ch_in_cos);
dma_ch_in_cos = -1;
}
int main()
{
vreg_set_voltage(VREG_VOLTAGE);
/* Step the USB PLL up to 192 MHz and overclock ADC with it. */
pll_init(pll_usb, 1, 1536 * MHZ, 4, 2);
clock_configure(clk_usb, 0, CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
4 * USB_CLK_KHZ * KHZ, USB_CLK_KHZ * KHZ);
clock_configure(clk_adc, 0, CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
4 * USB_CLK_KHZ * KHZ, 4 * USB_CLK_KHZ * KHZ);
/* Adjust system clock as well. */
set_sys_clock_khz(CLK_SYS_HZ / KHZ, true);
clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, CLK_SYS_HZ,
CLK_SYS_HZ);
@ -735,10 +466,16 @@ int main()
queue_init(&iq_queue, sizeof(uint8_t *), IQ_QUEUE_LEN);
rx_lo_init(frequency + sample_rate);
/* Init ADC */
adc_init();
gpio_disable_pulls(RX_PIN);
adc_gpio_init(RX_PIN);
adc_select_input(RX_PIN - 26);
adc_fifo_setup(true, true, 1, false, false);
adc_set_clkdiv(96.0f * ((ADC_RATE / ((float)sample_rate / DECIMATE)) - 1.0f) - 1.0f);
adc_run(true);
dma_t_samp = dma_claim_unused_timer(true);
dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE));
rx_lo_init(frequency + sample_rate);
while (true) {
if (check_command() > 0) {