update rx chain

i2s
xssfox 2025-07-06 19:09:36 +10:00
rodzic 81d4ca49b7
commit bb2f7b16a5
10 zmienionych plików z 306 dodań i 13 usunięć

3
.gitignore vendored
Wyświetl plik

@ -14,3 +14,6 @@ test_images
tx/tx_images/*.jpg
picam_temp*
*.log
benchmarking/generated/
benchmarking/samples/
benchmarking/stats.txt

Wyświetl plik

@ -37,7 +37,8 @@ NORMALISE = True
# sample_rate = input file sample rate.
SAMPLES = [
['wenet_sample_fs921416_float.bin', 115200, -100, 921416], # No threshold set, as signal is continuous.
# ['wenet_sample_fs921416_float.bin', 115200, -100, 921416], # No threshold set, as signal is continuous.
['wenet_sample_i2s_fs960000_float.bin', 96000, -100, 960000],
]

Wyświetl plik

@ -35,6 +35,12 @@ processing_type = {
"post_process" : " | wc -c", #
'files' : "./generated/wenet_sample_fs921416*.bin"
},
'wenet_i2s_demod': {
'demod': '| csdr convert_f_u8 | ../rx/fsk_demod --cu8 -s --stats=100 2 960000 96000 - - 2> stats.txt | ',
'decode': '../rx/wenet_ldpc - - 2> /dev/null ', #
"post_process" : " | wc -c", #
'files' : "./generated/wenet_sample_i2s_fs960000*.bin"
},
}
def run_analysis(mode, file_mask=None, shift=0.0, resample=1.0, verbose=False, log_output = None, dry_run = False, quick=False, show=False):

Wyświetl plik

@ -27,6 +27,7 @@ fi
: "${WEB_PORT:=5003}"
: "${IMAGE_PORT:=7890}"
: "${UPLOAD_ENABLE:=1}"
: "${FRAMING_MODE:=drs232_ldpc}"
# Start up the SSDV Uploader script and push it into the background.
if [ "$UPLOAD_ENABLE" == "1" ] ; then
@ -70,14 +71,14 @@ if [ "$SDR_TYPE" = "RTLSDR" ] ; then
echo "Using Complex Samples."
rtl_sdr -d "$DEVICE" -s "$SDR_RATE" -f "$RX_SSB_FREQ" -g "$GAIN" - | \
./fsk_demod --cu8 -s --stats=100 2 "$SDR_RATE" "$BAUD_RATE" - - 2> >(python3 fskstatsudp.py --rate 1 --freq $RX_SSB_FREQ --samplerate $SDR_RATE) | \
./drs232_ldpc - - -vv 2> /dev/null | \
./$FRAMING_MODE - - -vv 2> /dev/null | \
python3 rx_ssdv.py --partialupdate 16 --headless
elif [ "$SDR_TYPE" = "KA9Q" ] ; then
# Start receiver
echo "Starting pcmcat and demodulator"
pcmcat "$DEVICE" | \
./fsk_demod --cs16 -s --stats=100 2 "$SDR_RATE" "$BAUD_RATE" - - 2> >(python3 fskstatsudp.py --rate 1 --freq $RX_SSB_FREQ --samplerate $SDR_RATE --image_port $IMAGE_PORT) | \
./drs232_ldpc - - -vv 2> /dev/null | \
./$FRAMING_MODE - - -vv 2> /dev/null | \
python3 rx_ssdv.py --partialupdate 16 --headless --image_port $IMAGE_PORT
else
echo "No valid SDR type specified! Please enter RTLSDR or KA9Q!"

Wyświetl plik

@ -3,9 +3,10 @@
CC=gcc
CFLAGS= -O3 -Wall
all: fsk_demod drs232_ldpc
all: fsk_demod drs232_ldpc wenet_ldpc
@cp fsk_demod ../rx/
@cp drs232_ldpc ../rx/
@cp wenet_ldpc ../rx/
@echo "Binaries copied to ../rx/"
fsk_demod: fsk_demod.o fsk.o kiss_fft.o
@ -16,7 +17,11 @@ drs232_ldpc: drs232_ldpc.o mpdecode_core.o phi0.o
@gcc -o drs232_ldpc drs232_ldpc.o mpdecode_core.o phi0.o -lm
@echo "Built drs232_ldpc"
wenet_ldpc: wenet_ldpc.o mpdecode_core.o phi0.o
@gcc -o wenet_ldpc wenet_ldpc.o mpdecode_core.o phi0.o -lm
@echo "Built wenet_ldpc"
.c.o: $(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f fsk_demod drs232_ldpc *.o
rm -f fsk_demod drs232_ldpc wenet_ldpc *.o

266
src/wenet_ldpc.c 100755
Wyświetl plik

@ -0,0 +1,266 @@
/*---------------------------------------------------------------------------*\
FILE........: wenet_ldpc.c
AUTHOR......: David Rowe
DATE CREATED: Sep 2016
Looks for a unique word in series of soft decision symbols. When
found, LDPC decodes, and outputs a frame of packed bytes. Used for high bit
rate Horus SSTV reception.
Frame format:
16 bytes 0x55 - 0xabcdef01 UW - 256 bytes of payload - 2 bytes CRC - 65 bytes LPDC Parity
Each byte is encoded as a 10 bit RS232 serial word:
0 LSB .... MSB 1
Building:
$ gcc drs232_ldpc.c mpdecode_core.c -o drs232_ldpc -Wall -lm
\*---------------------------------------------------------------------------*/
/*
Copyright (C) 2016 David Rowe
All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License version 2.1, as
published by the Free Software Foundation. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "mpdecode_core.h"
/* Machine generated consts, H_rows, H_cols, test input/output data to
change LDPC code regenerate this file. */
#include "H2064_516_sparse.h"
/* states -----------------------------------------------*/
#define LOOK_FOR_UW 0
#define COLLECT_PACKET 1
/* packet parameters */
#define UW_BYTES 4
#define UW_BITS 32
#define UW_ALLOWED_ERRORS 4
#define BYTES_PER_PACKET 256
#define CRC_BYTES 2
#define PARITY_BYTES 65
#define BITS_PER_BYTE 8
#define UNPACKED_PACKET_BYTES ((UW_BYTES+BYTES_PER_PACKET+CRC_BYTES)*BITS_PER_BYTE)
#define SYMBOLS_PER_PACKET (BYTES_PER_PACKET+CRC_BYTES+PARITY_BYTES)*BITS_PER_BYTE
/* UW pattern we look for, including start/stop bits */
uint8_t uw[] = {
1,0,1,0,1,0,1,1,1,1,0,0,1,1,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,1
};
// from http://stackoverflow.com/questions/10564491/function-to-calculate-a-crc16-checksum
unsigned short gen_crc16(unsigned char* data_p, int length){
unsigned char x;
unsigned short crc = 0xFFFF;
while (length--){
x = crc >> 8 ^ *data_p++;
x ^= x>>4;
crc = (crc << 8) ^ ((unsigned short)(x << 12)) ^ ((unsigned short)(x <<5)) ^ ((unsigned short)x);
}
return crc;
}
int main(int argc, char *argv[]) {
FILE *fin, *fout;
int state, next_state, i, j, ind, score, verbose;
float symbol;
uint8_t bit, bit_buffer[UW_BITS];
double symbol_buf[SYMBOLS_PER_PACKET];
float llr[SYMBOLS_PER_PACKET];
uint8_t unpacked_packet[CODELENGTH];
uint8_t packet[BYTES_PER_PACKET+CRC_BYTES];
uint8_t abyte;
uint16_t tx_checksum, rx_checksum, packet_errors, packets;
int CodeLength, iter, parityCheckCount;
struct LDPC ldpc;
assert(sizeof(uw) == UW_BITS);
/* LDPC parameters */
CodeLength = CODELENGTH; /* length of entire codeword in bits */
/* set up LDPC code from include file constants */
ldpc.max_iter = MAX_ITER;
ldpc.dec_type = 0;
ldpc.q_scale_factor = 1;
ldpc.r_scale_factor = 1;
ldpc.CodeLength = CODELENGTH;
ldpc.NumberParityBits = NUMBERPARITYBITS;
ldpc.NumberRowsHcols = NUMBERROWSHCOLS;
ldpc.max_row_weight = MAX_ROW_WEIGHT;
ldpc.max_col_weight = MAX_COL_WEIGHT;
ldpc.H_rows = H_rows;
ldpc.H_cols = H_cols;
/* process command line ----------------------------------------------*/
if (argc < 3) {
fprintf(stderr, "usage: drs232 InputOneSymbolPerFloat OutputPackets [-v[v]]\n");
exit(1);
}
if (strcmp(argv[1], "-") == 0) fin = stdin;
else if ( (fin = fopen(argv[1],"rb")) == NULL ) {
fprintf(stderr, "Error opening input file: %s: %s.\n",
argv[1], strerror(errno));
exit(1);
}
if (strcmp(argv[2], "-") == 0) fout = stdout;
else if ( (fout = fopen(argv[2],"wb")) == NULL ) {
fprintf(stderr, "Error opening output file: %s: %s.\n",
argv[2], strerror(errno));
exit(1);
}
verbose = 0;
if (argc > 3) {
if (strcmp(argv[3], "-v") == 0) {
verbose = 1;
}
if (strcmp(argv[3], "-vv") == 0) {
verbose = 2;
}
}
state = LOOK_FOR_UW;
memset(bit_buffer,0, sizeof(bit_buffer));
packet_errors = packets = 0;
while(fread(&symbol, sizeof(float), 1, fin) == 1) {
/* make hard decision for purpose of UW detection */
bit = symbol < 0;
//printf("symbol; %f bit: %d\n", symbol, bit);
next_state = state;
if (state == LOOK_FOR_UW) {
/* put latest input bit into sliding buffer */
for(i=0; i<UW_BITS-1; i++) {
bit_buffer[i] = bit_buffer[i+1];
}
bit_buffer[i] = bit;
/* lets see if it matches the UW */
score = 0;
for(i=0; i<UW_BITS; i++) {
score += (bit_buffer[i] == uw[i]);
/* if (i == BITS_PER_BYTE)
printf(" ");
printf("%1d", unpacked_packet[i]); */
}
//printf("\n");
//fprintf(stderr,"UW score: %d\n", score);
if (score >= (UW_BITS-UW_ALLOWED_ERRORS)) {
//fprintf(stderr,"UW found! score: %d\n verbose: %d\n", score, verbose);
ind = 0;
next_state = COLLECT_PACKET;
}
}
if (state == COLLECT_PACKET) {
symbol_buf[ind++] = symbol;
if (ind == SYMBOLS_PER_PACKET) {
/* now LDPC decode */
sd_to_llr(llr, symbol_buf, CodeLength);
iter = run_ldpc_decoder(&ldpc, unpacked_packet, llr, &parityCheckCount);
/* pack into bytes */
for(i=0; i<BYTES_PER_PACKET+CRC_BYTES; i++) {
abyte = 0;
for(j=0; j<8; j++)
abyte |= unpacked_packet[8*i+j] << (7-j);
packet[i] = abyte;
}
/* then output if CRC check is OK */
rx_checksum = gen_crc16(packet, BYTES_PER_PACKET);
tx_checksum = packet[BYTES_PER_PACKET] + (packet[BYTES_PER_PACKET+1] << 8);
if (verbose == 2) {
if (rx_checksum != tx_checksum) {
fprintf(stderr, "tx_checksum: 0x%02x rx_checksum: 0x%02x\n",
tx_checksum, rx_checksum);
}
}
packets++;
if (rx_checksum == tx_checksum) {
fwrite(packet, sizeof(char), BYTES_PER_PACKET, fout);
fflush(fout);
}
else
packet_errors++;
if (verbose) {
fprintf(stderr, "packets: %d packet_errors: %d PER: %4.3f iter: %d\n",
packets, packet_errors,
(float)packet_errors/packets, iter);
}
//exit(0);
next_state = LOOK_FOR_UW;
}
}
//if (bits_read == (16*10 + UNPACKED_PACKET_BYTES))
// exit(0);
state = next_state;
}
fclose(fin);
fclose(fout);
fprintf(stderr, "packets: %d packet_errors: %d PER: %4.3f\n", packets, packet_errors,
(float)packet_errors/packets);
return 0;
}

Wyświetl plik

@ -40,6 +40,7 @@ UPLOAD_ENABLE=1
# Baud Rate & FSK Demod Oversampling Settings
#
# Default: 115177 baud 8x oversampling (Default using a RPi Zero W's UART)
# wenet v2: I2S 96000 baud, 10x oversampling
# Other parameters which *may* work, but are un-tested:
# 9600 baud, 100x oversampling
# 4800 baud, 200x oversampling
@ -64,6 +65,9 @@ IMAGE_PORT=7890
# (e.g. multiple docker containers), use a different port for each instance.
WEB_PORT=5003
# drs232_ldpc (traditional) or wenet_ldpc (wenet v2)
FRAMING_MODE=drs232_ldpc
# Stop and remove any existing wenet instances
echo "Stopping/Removing any existing Wenet instances..."
docker stop wenet || true && docker rm wenet || true
@ -84,6 +88,7 @@ if [ "$SDR_TYPE" = "RTLSDR" ] ; then
-e IMAGE_PORT=$IMAGE_PORT \
-e WEB_PORT=$WEB_PORT \
-e SDR_TYPE=$SDR_TYPE \
-e FRAMING_MODE=$FRAMING_MODE \
-e UPLOAD_ENABLE=$UPLOAD_ENABLE \
-v ~/wenet/rx_images/:/opt/wenet/rx_images/ \
--device /dev/bus/usb \
@ -104,6 +109,7 @@ elif [ "$SDR_TYPE" = "KA9Q" ] ; then
-e IMAGE_PORT=$IMAGE_PORT \
-e WEB_PORT=$WEB_PORT \
-e SDR_TYPE=$SDR_TYPE \
-e FRAMING_MODE=$FRAMING_MODE \
-e UPLOAD_ENABLE=$UPLOAD_ENABLE \
-v ~/wenet/rx_images/:/opt/wenet/rx_images/ \
-v /var/run/dbus:/var/run/dbus \

Wyświetl plik

@ -28,6 +28,8 @@ BIAS=0
# Note that this will need the rtl_biast utility available, which means
# building the rtl-sdr utils from this repo: https://github.com/rtlsdrblog/rtl-sdr
# drs232_ldpc (traditional) or wenet_ldpc (wenet v2)
FRAMING_MODE=drs232_ldpc
# Change the following path as appropriate.
# If running this from a .desktop file, you may need to set an absolute path here
@ -124,7 +126,7 @@ if [ "$RX_FLOW" = "IQ" ]; then
rtl_sdr -s $SDR_RATE -f $RX_SSB_FREQ -g $GAIN - | \
./fsk_demod --cu8 -s --stats=100 2 $SDR_RATE $BAUD_RATE - - 2> >(python fskdemodgui.py --wide) | \
./drs232_ldpc - - -vv 2> /dev/null | \
./$FRAMING_MODE - - -vv 2> /dev/null | \
python rx_ssdv.py --partialupdate 16
elif [ "$RX_FLOW" = "GQRX" ]; then
# GQRX Mode - take 48kHz real samples from GQRX via UDP.
@ -134,7 +136,7 @@ elif [ "$RX_FLOW" = "GQRX" ]; then
echo "Receiving samples from GQRX on UDP:localhost:7355"
nc -l -u localhost 7355 | \
./fsk_demod -s --stats=100 -b 1 -u 23500 2 48000 $BAUD_RATE - - 2> >(python fskdemodgui.py --wide) | \
./drs232_ldpc - - -vv 2> /dev/null | \
./$FRAMING_MODE - - -vv 2> /dev/null | \
python rx_ssdv.py --partialupdate 4
else
# If using a RTLSDR that has a DC spike (i.e. either has a FitiPower FC0012 or Elonics E4000 Tuner),
@ -145,7 +147,7 @@ else
csdr bandpass_fir_fft_cc 0.05 0.45 0.05 | csdr realpart_cf | \
csdr gain_ff 0.5 | csdr convert_f_s16 | \
./fsk_demod -s --stats=100 2 $SDR_RATE $BAUD_RATE - - 2> >(python fskdemodgui.py --wide) | \
./drs232_ldpc - - -vv 2> /dev/null | \
./$FRAMING_MODE - - -vv 2> /dev/null | \
python rx_ssdv.py --partialupdate 16
fi

Wyświetl plik

@ -33,6 +33,8 @@ BIAS=0
# Note that this will need the rtl_biast utility available, which means
# building the rtl-sdr utils from this repo: https://github.com/rtlsdrblog/rtl-sdr
# drs232_ldpc (traditional) or wenet_ldpc (wenet v2)
FRAMING_MODE=drs232_ldpc
# Change the following path as appropriate.
# If running this from a .desktop file, you may need to set an absolute path here
@ -67,8 +69,9 @@ RX_FLOW=IQ
#
# Modem Settings - Don't adjust these unless you really need to!
#
BAUD_RATE=115177 # Baud rate, in symbols/second.
OVERSAMPLING=8 # FSK Demod Oversampling rate. Not used in GQRX mode.
# TODO change back to traditional settings
BAUD_RATE=96000 # Baud rate, in symbols/second.
OVERSAMPLING=10 # FSK Demod Oversampling rate. Not used in GQRX mode.
# Known-Working Modem Settings:
# 115177 baud (Pi Zero W @ '115200' baud), 8x oversampling.
# 9600 baud, 100x oversampling.
@ -131,7 +134,7 @@ if [ "$RX_FLOW" = "IQ" ]; then
rtl_sdr -s $SDR_RATE -f $RX_SSB_FREQ -g $GAIN - | \
./fsk_demod --cu8 -s --stats=100 2 $SDR_RATE $BAUD_RATE - - 2> >(python fskstatsudp.py --rate 1 --freq $RX_SSB_FREQ --samplerate $SDR_RATE --image_port $IMAGE_PORT ) | \
./drs232_ldpc - - -vv 2> /dev/null | \
./$FRAMING_MODE - - -vv 2> /dev/null | \
python rx_ssdv.py --partialupdate 16 --headless --image_port $IMAGE_PORT
elif [ "$RX_FLOW" = "GQRX" ]; then
# GQRX Mode - take 48kHz real samples from GQRX via UDP.
@ -141,7 +144,7 @@ elif [ "$RX_FLOW" = "GQRX" ]; then
echo "Receiving samples from GQRX on UDP:localhost:7355"
nc -l -u localhost 7355 | \
./fsk_demod -s --stats=100 -b 1 -u 23500 2 48000 $BAUD_RATE - - 2> >(python fskstatsudp.py --rate 1 --freq $RX_SSB_FREQ --samplerate $SDR_RATE --real --image_port $IMAGE_PORT ) | \
./drs232_ldpc - - -vv 2> /dev/null | \
./$FRAMING_MODE - - -vv 2> /dev/null | \
python rx_ssdv.py --partialupdate 4 --headless --image_port $IMAGE_PORT
else
# If using a RTLSDR that has a DC spike (i.e. either has a FitiPower FC0012 or Elonics E4000 Tuner),
@ -152,7 +155,7 @@ else
csdr bandpass_fir_fft_cc 0.05 0.45 0.05 | csdr realpart_cf | \
csdr gain_ff 0.5 | csdr convert_f_s16 | \
./fsk_demod -s --stats=100 2 $SDR_RATE $BAUD_RATE - - 2> >(python fskstatsudp.py --rate 1 --freq $RX_SSB_FREQ --samplerate $SDR_RATE --real --image_port $IMAGE_PORT ) | \
./drs232_ldpc - - -vv 2> /dev/null | \
./$FRAMING_MODE - - -vv 2> /dev/null | \
python rx_ssdv.py --partialupdate 16 --headless --image_port $IMAGE_PORT
fi