add support for 12-bit dualchannel ADC (AD9238)

This commit is contained in:
Steve Markgraf 2025-11-16 00:25:51 +01:00
parent 642de0654d
commit 5867fdb15c
4 changed files with 119 additions and 10 deletions

View file

@ -2,6 +2,7 @@
#define __FORMAT_CONVERT_H #define __FORMAT_CONVERT_H
void hsdaoh_unpack_pio_12bit(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info); void hsdaoh_unpack_pio_12bit(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info);
void hsdaoh_unpack_pio_dualchan_12bit(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info);
void hsdaoh_unpack_pio_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info); void hsdaoh_unpack_pio_12bit_dual(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info);
void hsdaoh_unpack_pio_10bit_iq(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info); void hsdaoh_unpack_pio_10bit_iq(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info);
void hsdaoh_unpack_pio_8bit_iq(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info); void hsdaoh_unpack_pio_8bit_iq(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info);

View file

@ -45,7 +45,7 @@ struct hsdaoh_dev {
unsigned int width, height, fps; unsigned int width, height, fps;
bool output_float; bool output_float;
iqconverter_float_t *cnv_f; iqconverter_float_t *cnv_f1, *cnv_f2;
/* status */ /* status */
int dev_lost; int dev_lost;
@ -108,6 +108,29 @@ enum
PIO_32BIT, PIO_32BIT,
PIO_32BIT_IQ, PIO_32BIT_IQ,
PIO_PCM1802_AUDIO, PIO_PCM1802_AUDIO,
PIO_AUDIO_PLACEHOLDER1,
PIO_AUDIO_PLACEHOLDER2,
PIO_AUDIO_PLACEHOLDER3,
PIO_DUALCHAN_1BIT,
PIO_DUALCHAN_1BIT_IQ,
PIO_DUALCHAN_2BIT,
PIO_DUALCHAN_2BIT_IQ,
PIO_DUALCHAN_4BIT,
PIO_DUALCHAN_4BIT_IQ,
PIO_DUALCHAN_8BIT,
PIO_DUALCHAN_8BIT_IQ,
PIO_DUALCHAN_10BIT,
PIO_DUALCHAN_10BIT_IQ,
PIO_DUALCHAN_12BIT,
PIO_DUALCHAN_12BIT_IQ,
PIO_DUALCHAN_14BIT,
PIO_DUALCHAN_14BIT_IQ,
PIO_DUALCHAN_16BIT,
PIO_DUALCHAN_16BIT_IQ,
PIO_DUALCHAN_24BIT,
PIO_DUALCHAN_24BIT_IQ,
PIO_DUALCHAN_32BIT,
PIO_DUALCHAN_32BIT_IQ,
// Placeholder for internal ADC data from pico // Placeholder for internal ADC data from pico
FPGA_1BIT = 256, FPGA_1BIT = 256,
FPGA_2BIT, FPGA_2BIT,

View file

@ -32,10 +32,9 @@
#include <hsdaoh_private.h> #include <hsdaoh_private.h>
#include <format_convert.h> #include <format_convert.h>
static inline void hsdaoh_16bit_to_float(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info, uint16_t *buf, size_t length, float scale, bool duplicate) static inline void hsdaoh_16bit_to_float(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info, uint16_t *buf, size_t length, float scale, bool conv_iq)
{ {
unsigned int i, j = 0; unsigned int i, j = 0;
float *floats = malloc(sizeof(float) * dev->width * dev->height * 2 * 2); float *floats = malloc(sizeof(float) * dev->width * dev->height * 2 * 2);
if (!floats) if (!floats)
@ -44,12 +43,45 @@ static inline void hsdaoh_16bit_to_float(hsdaoh_dev_t *dev, hsdaoh_data_info_t *
for (unsigned int i = 0; i < length; i++) { for (unsigned int i = 0; i < length; i++) {
float sample_i = buf[i]; float sample_i = buf[i];
floats[j++] = (sample_i - scale) * (1/scale); floats[j++] = (sample_i - scale) * (1/scale);
if (duplicate)
floats[j++] = (sample_i - scale) * (1/scale);
} }
// iqconverter_float_process(dev->cnv_f, (float *) floats, j); if (conv_iq) {
if (data_info->stream_id == 0)
iqconverter_float_process(dev->cnv_f1, (float *) floats, j);
else
iqconverter_float_process(dev->cnv_f2, (float *) floats, j);
}
data_info->buf = (uint8_t *)floats;
data_info->len = j * sizeof(float);
data_info->bits_per_samp = 32;
data_info->nchans = 1;
data_info->is_signed = true;
data_info->is_float = true;
dev->cb(data_info);
free(floats);
}
static inline void hsdaoh_signed_12bit_to_float(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info, uint16_t *buf, size_t length, float scale, bool conv_iq)
{
unsigned int i, j = 0;
float *floats = malloc(sizeof(float) * dev->width * dev->height * 2 * 2);
if (!floats)
return;
for (unsigned int i = 0; i < length; i++) {
int16_t samp = (int16_t)(buf[i] << 4);
floats[j++] = (samp >> 4) * (1/scale);
}
if (conv_iq) {
if (data_info->stream_id == 0)
iqconverter_float_process(dev->cnv_f1, (float *) floats, j);
else
iqconverter_float_process(dev->cnv_f2, (float *) floats, j);
}
data_info->buf = (uint8_t *)floats; data_info->buf = (uint8_t *)floats;
data_info->len = j * sizeof(float); data_info->len = j * sizeof(float);
@ -96,6 +128,53 @@ void hsdaoh_unpack_pio_12bit(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info)
free(out); free(out);
} }
// Same format as PIO 12 bit, but with two interleaved ADC channels
void hsdaoh_unpack_pio_dualchan_12bit(hsdaoh_dev_t *dev, hsdaoh_data_info_t *data_info)
{
uint16_t *in = (uint16_t *)data_info->buf;
size_t inlen = data_info->len / sizeof(uint16_t);
uint16_t *out16_1 = malloc(sizeof(uint16_t) * dev->width * dev->height);
uint16_t *out16_2 = malloc(sizeof(uint16_t) * dev->width * dev->height);
unsigned int j = 0;
for (unsigned int i = 0; i < inlen; i += 3) {
out16_1[j ] = (in[i+2] & 0xf000) >> 4 | (in[i+1] & 0xf000) >> 8 | (in[i] >> 12);
out16_2[j++] = in[i ] & 0x0fff;
out16_1[j ] = in[i+1] & 0x0fff;
out16_2[j++] = in[i+2] & 0x0fff;
}
data_info->bits_per_samp = 12;
data_info->nchans = 1;
if (dev->output_float) {
hsdaoh_signed_12bit_to_float(dev, data_info, out16_1, j, 2047.5, true);
} else {
data_info->buf = (uint8_t *)out16_1;
data_info->len = j * sizeof(uint16_t);
data_info->bits_per_samp = 12;
data_info->nchans = 1;
data_info->is_signed = true;
data_info->is_float = false;
dev->cb(data_info);
}
data_info->stream_id += 1;
if (dev->output_float) {
hsdaoh_16bit_to_float(dev, data_info, out16_2, j, 2047.5, true);
} else {
data_info->buf = (uint8_t *)out16_2;
dev->cb(data_info);
}
free(out16_1);
free(out16_2);
}
// We receive three 32-bit words containing four 24-bit samples (sample A - D) // We receive three 32-bit words containing four 24-bit samples (sample A - D)
// First word: A07 A06 A05 A04 A03 A02 A01 A00 B23 B22 B21 B20 B19 B18 B17 B16 B15 B14 B13 B12 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00 // First word: A07 A06 A05 A04 A03 A02 A01 A00 B23 B22 B21 B20 B19 B18 B17 B16 B15 B14 B13 B12 B11 B10 B09 B08 B07 B06 B05 B04 B03 B02 B01 B00
// Second word: A15 A14 A13 A12 A11 A10 A09 A08 C23 C22 C21 C20 C19 C18 C17 C16 C15 C14 C13 C12 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00 // Second word: A15 A14 A13 A12 A11 A10 A09 A08 C23 C22 C21 C20 C19 C18 C17 C16 C15 C14 C13 C12 C11 C10 C09 C08 C07 C06 C05 C04 C03 C02 C01 C00

View file

@ -490,7 +490,8 @@ int hsdaoh_open(hsdaoh_dev_t **out_dev, uint32_t index)
goto err; goto err;
dev->dev_lost = 0; dev->dev_lost = 0;
dev->cnv_f = iqconverter_float_create(HB_KERNEL_FLOAT, HB_KERNEL_FLOAT_LEN); dev->cnv_f1 = iqconverter_float_create(HB_KERNEL_FLOAT, HB_KERNEL_FLOAT_LEN);
dev->cnv_f2 = iqconverter_float_create(HB_KERNEL_FLOAT, HB_KERNEL_FLOAT_LEN);
found: found:
*out_dev = dev; *out_dev = dev;
@ -522,7 +523,8 @@ int hsdaoh_close(hsdaoh_dev_t *dev)
uvc_unref_device(dev->uvc_dev); uvc_unref_device(dev->uvc_dev);
uvc_exit(dev->uvc_ctx); uvc_exit(dev->uvc_ctx);
iqconverter_float_free(dev->cnv_f); iqconverter_float_free(dev->cnv_f1);
iqconverter_float_free(dev->cnv_f2);
free(dev); free(dev);
return 0; return 0;
@ -564,6 +566,9 @@ void hsdaoh_output(hsdaoh_dev_t *dev, uint16_t sid, int format, uint32_t srate,
case PIO_12BIT_DUAL: case PIO_12BIT_DUAL:
hsdaoh_unpack_pio_12bit_dual(dev, &data_info); hsdaoh_unpack_pio_12bit_dual(dev, &data_info);
break; break;
case PIO_DUALCHAN_12BIT:
hsdaoh_unpack_pio_dualchan_12bit(dev, &data_info);
break;
case PIO_PCM1802_AUDIO: case PIO_PCM1802_AUDIO:
hsdaoh_unpack_pio_pcm1802_audio(dev, &data_info); hsdaoh_unpack_pio_pcm1802_audio(dev, &data_info);
break; break;
@ -837,7 +842,8 @@ int hsdaoh_start_stream(hsdaoh_dev_t *dev, hsdaoh_read_cb_t cb, void *ctx, unsig
if (HSDAOH_INACTIVE != dev->async_status) if (HSDAOH_INACTIVE != dev->async_status)
return -2; return -2;
iqconverter_float_reset(dev->cnv_f); iqconverter_float_reset(dev->cnv_f1);
iqconverter_float_reset(dev->cnv_f2);
dev->async_status = HSDAOH_RUNNING; dev->async_status = HSDAOH_RUNNING;
dev->async_cancel = 0; dev->async_cancel = 0;