Compare commits
6 commits
b52f772845
...
aea84609b2
| Author | SHA1 | Date | |
|---|---|---|---|
| aea84609b2 | |||
| f45d343c8c | |||
| 558c3a0a41 | |||
| 2ddd7ddd71 | |||
| d894929628 | |||
| b0a3aeb50b |
2 changed files with 79 additions and 40 deletions
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
Using RP2040 / Raspberry Pi Pico as a software-defined radio receiver.
|
||||
|
||||
See the [blog post](https://blog.porucha.net/2024/pico-sdr/) for more information. Older code the article is mostly referring to can be found in the branch `old`.
|
||||
See the [blog post](https://blog.porucha.net/2024/pico-sdr/) for more information. Older code the article is mostly referring to can be found in the branch `old` and a more up-to-date approach in the branch `master`.
|
||||
|
||||
This branch contains code to use RP2040 and some passives as a superheterodyne receiver. It is still very much work in progress.
|
||||
|
||||
## Circuit
|
||||
|
||||

|
||||
Please refer to [the simulation](https://www.falstad.com/circuit/circuitjs.html?ctz=CQAgjCBMB0CsCmBaMAGEAOW0AsLboHYA2SWSIsWMIkAtFaFFCWNBZMAKEoE5wV06ENiE8ekYULZpUTOfM4BzfoMnhU2NfU4AlED2ybERTeh41jmmfpHyZ0IrHAOn9OJ0goC4AMyQhiOa+RGiBNE4AkgBi3ESihiBh-LiJQehgBlAgPtA+INp6BkZ+NprUwiBOPnZy2bn5DQywnABOtGSJGRIEHUkydpwA7smhXSOpNCicAMaVIRMgjhZBMkjeyA7YYjzoXiGwPuJGYC6cADaLsMs0PRJ9+Yzy288vz4k5B8REPGAi2HSQHzoNb5GbtO5BW6NcBIIQnEzbIgmMCQOiwWDEZyOJTghpQ8raYZQpLoEQLKbDUkBIJFcmtbJxBbYEzkypTTwQVEQmiQSBGFY2ELySS1BhPV4SsTgcRyTgRcbIcSlTpK6w5HxDOahIJXNBkqYANygBG5DOpk3AGHQnmF9Ea7jaGm1NDAYB81warBxbv8DR9cItU1m-oWIbtYFhiTFzDi4lYkDA1oIQJQVnO4EBppD92jcklkqjj2YifQPhN5BIBgIgm8FOViokVJVEjrTYbuPbUwARhgiOs+TJsK79LXOAAPXtORCkPgGPjTq4VPkgHQABU8uHHtBNiU8eWtRhtFXKAHFVxEAPIAHQAzs0J8R56gaNaIMgUHkyjQz5fb+hNU6iQsoBJR1mIEhkrqWhbgQBAQIcGCAsIEHgN+57XjePCalBSTMvykycD2GRvpQEDMnkiBAqCE7ugE1A0ORnREJ+UCaAAggAdgALvAHEcQAhmCrruqGmasrmTAQPCQJXL8sDiD4PhuhiWLNAA9tklQon6PiwNgPhWHAZZIjsyImO6PCuEWEBoBIeQakAA).
|
||||
|
||||
## Software
|
||||
|
||||
|
|
@ -32,4 +34,4 @@ See the [blog post](https://blog.porucha.net/2024/pico-sdr/) for more informatio
|
|||
|
||||
4. Open `grc/PicoSDR-WBFM.grc` in GNU Radio Companion, adjust carrier frequency to match your favorite FM radio station and press `F6`.
|
||||
|
||||
5. Alternatively [gqrx](https://www.gqrx.dk/) works fine with `rtl_tcp` input mode. Maximum sample rate seem to be 400 ksps, above that the samples are dropped. Make sure to adjust LNA gain to +30 dB. It's not accurate, but it does control bias strength which in turn does affect analog gain.
|
||||
5. Alternatively [gqrx](https://www.gqrx.dk/) works fine with `rtl_tcp` input mode. Maximum sample rate seem to be 400 ksps, above that the samples are dropped. Make sure to set LNA gain to 0, gain control is digital and does not provide any benefits unless you lower your sampling rate significantly.
|
||||
|
|
|
|||
111
src/main.c
111
src/main.c
|
|
@ -25,8 +25,8 @@
|
|||
#define CLK_SYS_HZ (300 * MHZ)
|
||||
|
||||
#define LO_PIN 9
|
||||
#define RX_PIN 10
|
||||
#define FB_PIN 11
|
||||
#define RX_PIN 8
|
||||
#define FB_PIN 5
|
||||
#define PSU_PIN 23
|
||||
|
||||
#define PIO pio1
|
||||
|
|
@ -37,15 +37,15 @@
|
|||
|
||||
#define IQ_SAMPLES 32
|
||||
#define IQ_BLOCK_LEN (2 * IQ_SAMPLES)
|
||||
#define IQ_QUEUE_LEN 4
|
||||
#define IQ_QUEUE_LEN 8
|
||||
|
||||
#define LO_BITS_DEPTH 15
|
||||
#define LO_WORDS (1 << (LO_BITS_DEPTH - 2))
|
||||
static uint32_t lo_cos[LO_WORDS] __attribute__((__aligned__(1 << LO_BITS_DEPTH)));
|
||||
|
||||
#define DECIMATE 4
|
||||
#define DECIMATE 16
|
||||
#define RX_STRIDE (2 * IQ_SAMPLES * DECIMATE)
|
||||
#define RX_BITS_DEPTH 13
|
||||
#define RX_BITS_DEPTH 14
|
||||
#define RX_WORDS (1 << (RX_BITS_DEPTH - 2))
|
||||
|
||||
static_assert(RX_STRIDE * 4 <= RX_WORDS, "RX_STRIDE * 4 <= RX_WORDS");
|
||||
|
|
@ -129,11 +129,11 @@ static void init_fb()
|
|||
|
||||
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_FAST);
|
||||
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(1),
|
||||
pio_encode_delay(0),
|
||||
//pio_encode_nop() | pio_encode_sideset(1, 0) | pio_encode_delay(0),
|
||||
};
|
||||
|
||||
|
|
@ -382,36 +382,71 @@ static void rf_rx_stop(void)
|
|||
dma_t_samp = -1;
|
||||
}
|
||||
|
||||
inline static int next_sample(const uint32_t *buf, int *h)
|
||||
struct IQ {
|
||||
int I, Q;
|
||||
};
|
||||
|
||||
inline static struct IQ next_sample(const uint32_t *buf)
|
||||
{
|
||||
int x1 = 2 * (buf[0] - buf[1]);
|
||||
int x2 = 0;
|
||||
int x3 = -2 * (buf[4] - buf[5]);
|
||||
int x4 = 0;
|
||||
int x00 = buf[0] - buf[1];
|
||||
int x01 = buf[2] - buf[3];
|
||||
int x02 = buf[4] - buf[5];
|
||||
int x03 = buf[6] - buf[7];
|
||||
int x04 = buf[8] - buf[9];
|
||||
int x05 = buf[10] - buf[11];
|
||||
int x06 = buf[12] - buf[13];
|
||||
int x07 = buf[14] - buf[15];
|
||||
int x08 = buf[16] - buf[17];
|
||||
int x09 = buf[18] - buf[19];
|
||||
int x10 = buf[20] - buf[21];
|
||||
int x11 = buf[22] - buf[23];
|
||||
int x12 = buf[24] - buf[25];
|
||||
int x13 = buf[26] - buf[27];
|
||||
int x14 = buf[28] - buf[29];
|
||||
int x15 = buf[30] - buf[31];
|
||||
|
||||
int sample = 18 * h[9] + 14 * h[8] - 32 * h[7] - 33 * h[6] + 106 * h[5] + 362 * h[4] +
|
||||
565 * h[3] + 565 * h[2] + 362 * h[1] + 106 * h[0] - 33 * x1 - 32 * x2 +
|
||||
14 * x3 + 18 * x4;
|
||||
int I = 0;
|
||||
I += 39 * x15;
|
||||
I += 72 * x14;
|
||||
I += 94 * x13;
|
||||
I += 102 * x12;
|
||||
I += 94 * x11;
|
||||
I += 72 * x10;
|
||||
I += 39 * x09;
|
||||
I += 0 * x08;
|
||||
I += -39 * x07;
|
||||
I += -72 * x06;
|
||||
I += -94 * x05;
|
||||
I += -102 * x04;
|
||||
I += -94 * x03;
|
||||
I += -72 * x02;
|
||||
I += -39 * x01;
|
||||
I += 0 * x00;
|
||||
|
||||
h[9] = h[5];
|
||||
h[8] = h[4];
|
||||
h[7] = h[3];
|
||||
h[6] = h[2];
|
||||
h[5] = h[1];
|
||||
h[4] = h[0];
|
||||
h[3] = x1;
|
||||
h[2] = x2;
|
||||
h[1] = x3;
|
||||
h[0] = x4;
|
||||
int Q = 0;
|
||||
Q += 94 * x15;
|
||||
Q += 72 * x14;
|
||||
Q += 39 * x13;
|
||||
Q += 0 * x12;
|
||||
Q += -39 * x11;
|
||||
Q += -72 * x10;
|
||||
Q += -94 * x09;
|
||||
Q += -102 * x08;
|
||||
Q += -94 * x07;
|
||||
Q += -72 * x06;
|
||||
Q += -39 * x05;
|
||||
Q += 0 * x04;
|
||||
Q += 39 * x03;
|
||||
Q += 72 * x02;
|
||||
Q += 94 * x01;
|
||||
Q += 102 * x00;
|
||||
|
||||
return sample >> 10;
|
||||
return (struct IQ){ gain * I / 1024, gain * Q / 1024 };
|
||||
}
|
||||
|
||||
static void rf_rx(void)
|
||||
{
|
||||
const uint32_t base = (uint32_t)rx_cos;
|
||||
int prevI[10] = { 0 };
|
||||
int prevQ[10] = { 0 };
|
||||
int pos = 0;
|
||||
|
||||
while (true) {
|
||||
|
|
@ -424,8 +459,6 @@ static void rf_rx(void)
|
|||
int head = (dma_hw->ch[dma_ch_in_cos].write_addr - base) / 4;
|
||||
int delta = (head < pos ? head + RX_WORDS : head) - pos;
|
||||
|
||||
sleep_us(10);
|
||||
|
||||
while (delta < RX_STRIDE) {
|
||||
sleep_us(1);
|
||||
head = (dma_hw->ch[dma_ch_in_cos].write_addr - base) / 4;
|
||||
|
|
@ -449,12 +482,14 @@ static void rf_rx(void)
|
|||
int64_t max_amplitude = CLK_SYS_HZ / 2 / sample_rate;
|
||||
|
||||
for (int i = 0; i < IQ_SAMPLES; i++) {
|
||||
int64_t I = next_sample(cos_ptr + 0, prevI);
|
||||
int64_t Q = next_sample(cos_ptr + 2, prevQ);
|
||||
cos_ptr += 8;
|
||||
struct IQ IQ = next_sample(cos_ptr);
|
||||
cos_ptr += 2 * DECIMATE;
|
||||
|
||||
I *= gain;
|
||||
I -= (max_amplitude * 181) / 256;
|
||||
int64_t I = IQ.I;
|
||||
int64_t Q = IQ.Q;
|
||||
|
||||
//I *= gain;
|
||||
I -= (max_amplitude * 169) / 256;
|
||||
I /= max_amplitude;
|
||||
|
||||
if (I > 127)
|
||||
|
|
@ -464,8 +499,8 @@ static void rf_rx(void)
|
|||
|
||||
*blockptr++ = (uint8_t)I + 128;
|
||||
|
||||
Q *= gain;
|
||||
Q -= (max_amplitude * 181) / 256;
|
||||
//Q *= gain;
|
||||
Q -= (max_amplitude * 169) / 256;
|
||||
Q /= max_amplitude;
|
||||
|
||||
if (Q > 127)
|
||||
|
|
@ -563,6 +598,8 @@ static void do_rx()
|
|||
if (queue_try_remove(&iq_queue, &block)) {
|
||||
fwrite(block, IQ_BLOCK_LEN, 1, stdout);
|
||||
fflush(stdout);
|
||||
} else {
|
||||
sleep_us(25);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue