3
0
mirror of https://github.com/Qortal/Brooklyn.git synced 2025-01-30 14:52:17 +00:00

Remove dsat from mainstream

This commit is contained in:
Scare Crowe 2021-11-11 19:07:47 +05:00
parent b5515bad0d
commit db0c4cb4dc
13 changed files with 0 additions and 1074 deletions

View File

@ -1,5 +0,0 @@
#!/bin/bash -x
# automate local build of codec2
git clone https://github.com/drowe67/codec2.git
cd codec2 && mkdir -p build_linux && cd build_linux
cmake ../ && make

View File

@ -1,5 +0,0 @@
#!/bin/bash -x
# Automate building of csdr library, which is used by rtl_fsk application
PIRIP_DIR=$(pwd)
git clone https://github.com/ha7ilm/csdr.git
cd csdr && make

View File

@ -1,9 +0,0 @@
# automate building of RpiTx library
git clone https://github.com/F5OEO/librpitx
cd librpitx/src
make
cd ../../
echo "To the end of /boot/config.txt add"
echo " gpu_freq=250"
echo " force_turbo=1"
echo "and reboot your Pi"

View File

@ -1,10 +0,0 @@
#!/bin/bash -x
# Automate building of rtlsdr library and applications
PIRIP_DIR=$(pwd)
git clone https://github.com/drowe67/librtlsdr.git
cd librtlsdr
git checkout development
mkdir -p build_rtlsdr
cd build_rtlsdr
cmake ../ -DINSTALL_UDEV_RULES=ON -DCODEC2_BUILD_DIR=${PIRIP_DIR}/codec2/build_linux -DCSDR_BUILD_DIR=${PIRIP_DIR}/csdr
make rtl_sdr rtl_fsk

View File

@ -1,70 +0,0 @@
#!/usr/bin/env python3
#
# rtl_fsk modem dashboard
#
# usage:
# netcat -luk 8001 | ./dash.py
import json
import sys
import numpy as np
import matplotlib.pyplot as plt
import io
roll_s = 5
SNRestdB = []
norm_timing = [];
# unbuffered stdin
sys.stdin = io.open(sys.stdin.fileno())
for line in sys.stdin:
data = json.loads(line)
# update rolling SNRest
SNRest_lin = data['SNRest_lin']
tmp = 10*np.log10(SNRest_lin+1)
SNRestdB.append(tmp)
if len(SNRestdB) > roll_s:
SNRestdB.pop(0)
# update rolling norm timing
latest_norm_timing = data['norm_rx_timing']
norm_timing.extend(latest_norm_timing)
one_sec = len(latest_norm_timing)
if len(norm_timing) > roll_s*one_sec:
norm_timing = norm_timing[one_sec:]
plt.clf()
plt.subplot(311)
SfdB = data['SfdB']
fsk_lower_kHz = np.array(data['fsk_lower_Hz'])/1000
fsk_upper_kHz = np.array(data['fsk_upper_Hz'])/1000
f_est_kHz = np.array(data['f_est_Hz'])/1000
Fs_kHz = data['Fs_Hz']/1000
f_axis_kHz = -Fs_kHz/2 + np.arange(len(SfdB))*Fs_kHz/len(SfdB)
plt.plot(f_axis_kHz, SfdB)
height = 10;
mn = np.min(SfdB);
for f in f_est_kHz:
plt.plot(f, mn,"r+")
plt.plot([fsk_lower_kHz, fsk_lower_kHz], [mn, mn+height],"g")
plt.plot([fsk_upper_kHz, fsk_upper_kHz], [mn, mn+height],"g")
plt.grid()
plt.ylabel('Spectrum')
plt.subplot(312)
plt.plot(SNRestdB)
plt.ylabel('SNR')
mx = 10*np.ceil(np.max(SNRestdB)/10);
plt.ylim([0, mx])
plt.grid()
plt.subplot(313)
plt.plot(norm_timing)
plt.ylabel('Timing')
plt.ylim([-0.5, 0.5])
plt.draw()
plt.pause(0.01) # renders plot

View File

@ -1,90 +0,0 @@
#!/bin/bash
# frame repeater service
# TODO
# [X] way to send stderr to log file
# [ ] way to set freq at start up
# [ ] way to set Rs and M at start up
# [ ] way to start as frame repeater or beacon
TERM_ADDR=0x2
RS=10000
DATA_BITS_PER_FRAME=256
CODE=H_${DATA_BITS_PER_FRAME}_512_4
TR_SWITCH_GPIO=21
PIRIP_PATH=/home/pi/pirip
NAME=frame_repeater
PIDFILE=/var/run/${NAME}.pid
LOGFILE=/var/log/${NAME}.log
PATH=${PIRIP_PATH}/tx:${PIRIP_PATH}/librtlsdr/build_rtlsdr/src:${PATH}
GAIN="${GAIN:-49}"
function tx_burst_rpitx {
num_bursts=$1
rpitx_fsk /dev/zero --code H_256_512_4 -r ${RS} -s ${RS} --testframes 3 --bursts ${num_bursts} --delay 2000 -g 21 -m 2
#rpitx_fsk -c
}
function start_rx {
rtl_fsk -g ${GAIN} -f 144490000 - -a 200000 -r ${RS} --code H_256_512_4 --mask ${RS} -L $1 > /dev/null &
echo $!>${PIDFILE}
}
function stop_service {
echo "service stopping - bye!" 1>&2
parent=$(cat ${PIDFILE})
kill ${parent}
rm ${PIDFILE}
}
case "$1" in
start)
( rtl_fsk -g 49 -f 144490000 - -a 200000 -r ${RS} --code ${CODE} --mask ${RS} --filter ${TERM_ADDR} -q -b | \
frame_repeater ${DATA_BITS_PER_FRAME} ${TERM_ADDR} | \
rpitx_fsk - --code ${CODE} -r ${RS} -s ${RS} -g ${TR_SWITCH_GPIO} --packed -- ) >>${LOGFILE} 2>&1 &
echo $!>${PIDFILE}
;;
start_verbose)
# run in the foreground and log to stderr instead of a logfile
rtl_fsk -g 49 -f 144490000 - -a 200000 -r ${RS} --code ${CODE} --mask ${RS} --filter ${TERM_ADDR} -q -b -v | \
frame_repeater ${DATA_BITS_PER_FRAME} ${TERM_ADDR} | \
rpitx_fsk - --code ${CODE} -r ${RS} -s ${RS} -g ${TR_SWITCH_GPIO} --packed --
;;
start_loopback)
# Send packets from Pi to RTLSDR on this machine (no filtering of packets or frame repeater)
set -x
verbose=1
start_rx
sleep 1
if ! ps -p $(cat ${PIDFILE}); then
echo "rtl_fsk failed to start!"
rm ${PIDFILE}
exit 1
fi
tx_burst_rpitx $2
stop_service
;;
stop)
stop_service
;;
restart)
$0 stop
$0 start
;;
status)
if [ -e ${PIDFILE} ]; then
echo ${NAME} is running, pid=`cat ${PIDFILE}`
else
echo$ {NAME} is NOT running
exit 1
fi
;;
*)
echo "Usage: sudo $0 {start|stop|status|restart}"
echo ""
echo "start - Start frame repeater service, logfile is ${LOGFILE}"
echo "start_verbose - Start frame repeater in foreground, no logfile"
echo "start_loopback numBursts - Local loopback test, tx/rx numBursts"
esac
exit 0

View File

@ -1,130 +0,0 @@
#!/bin/bash
# ping service script - sends bursts to the frame repeater and logs reply
# TODO
# [X] receive it's own packets
# [X] log one one logfile
# [X] clean start up and shut down
# [X] send a sequence of packets
# [X] test with Pi running frame_repeater OTC
TERM_ADDR=0x1
RS=10000
DATA_BITS_PER_FRAME=256
CODE=H_${DATA_BITS_PER_FRAME}_512_4
PIRIP_PATH=${PWD}/..
NAME=ping
PIDFILE1=/var/run/${NAME}1.pid
PIDFILE2=/var/run/${NAME}2.pid
LOGFILE=/var/log/${NAME}.log
PATH=${PIRIP_PATH}/tx:${PIRIP_PATH}/librtlsdr/build_rtlsdr/src:${PATH}
PATH=${PIRIP_PATH}/codec2/build_linux/misc:${PIRIP_PATH}/codec2/build_linux/src:${PATH}
PAUSE="${PAUSE:-10}"
GAIN="${GAIN:-49}"
HACKRF_TX_GAIN="${HACKRF_TX_GAIN:-40}"
function tx_burst_hackrf {
num_bursts=$1
pause_between_bursts=$2
tmp=$(mktemp)
if [ -z ${verbose+x} ]; then
# quiet version
freedv_data_raw_tx --source 0x1 -c --testframes 3 --burst 1 --Fs 100000 --Rs ${RS} --tone1 ${RS} --shift ${RS} -a 30000 FSK_LDPC /dev/zero - 2>/dev/null | tlininterp - - 40 -d -f > ${tmp}
for (( i=1; i<=$num_bursts; i++ ))
do
printf "%d Tx burst %d from HackRF...\n" `date +%s` ${i} 1>&2
hackrf_transfer -t ${tmp} -s 4E6 -f 143.5E6 -x ${HACKRF_TX_GAIN} 2>/dev/null 1>/dev/null
sleep $pause_between_bursts
done
else
# verbose version
freedv_data_raw_tx --source 0x1 -c --testframes 3 --burst 1 --Fs 100000 --Rs ${RS} --tone1 ${RS} --shift ${RS} -a 30000 FSK_LDPC /dev/zero - | tlininterp - - 40 -d -f > ${tmp}
for (( i=1; i<=$num_bursts; i++ ))
do
printf "%d Tx burst %d from HackRF...\n" `date +%s` ${i} 1>&2
hackrf_transfer -t ${tmp} -s 4E6 -f 143.5E6 -x ${HACKRF_TX_GAIN}
sleep $pause_between_bursts
done
fi
}
function start_rx {
rtl_fsk -g ${GAIN} -f 144490000 - -a 200000 -r 10000 --code H_256_512_4 --mask 10000 -L $1 > /dev/null &
echo $!>${PIDFILE2}
}
function stop_service {
echo "service stopping - bye!" 1>&2
if [ -e ${PIDFILE2} ]; then
pid2=$(cat ${PIDFILE2})
rm ${PIDFILE2}
kill ${pid2}
fi
if [ -e ${PIDFILE1} ]; then
pid1=$(cat ${PIDFILE1})
rm ${PIDFILE1}
kill ${pid1}
fi
}
function check_running {
if [ -e ${PIDFILE1} ]; then
echo "service already running... pid: ${PIDFILE1}"
exit 1
fi
if [ -e ${PIDFILE2} ]; then
echo "service already running... pid: ${PIDFILE2}"
exit 1
fi
}
case "$1" in
start)
check_running
( start_rx "--filter ${TERM_ADDR}" && sleep 1 && tx_burst_hackrf $2 ${PAUSE} && stop_service) 2>>${LOGFILE} &
echo $!>${PIDFILE1}
;;
start_verbose)
check_running
# Show all tool outputs and log output to stderr rather than logfile
verbose=1
start_rx "--filter ${TERM_ADDR}" && sleep 1 && tx_burst_hackrf 1 1 && stop_service
;;
start_loopback)
check_running
# Send packets from HackRF to RTLSDR on this machine (no filtering of packets)
verbose=1
start_rx && sleep 1 && tx_burst_hackrf 1 1 && stop_service
;;
start_carrier)
# Send carrier from HackRF at same Tx power as FSK signal, much easier to measure on a spec-an
fsk_get_test_bits - 60000 | fsk_mod -t -c -a 30000 2 40000 1000 1000 2000 - - | \
tlininterp - - 100 -d -f | \
hackrf_transfer -t - -s 4E6 -f 143.5E6 -x ${HACKRF_TX_GAIN}
;;
stop)
stop_service
;;
restart)
$0 stop
$0 start
;;
status)
if [ -e ${PIDFILE1} ]; then
echo ${NAME} is running, pid=`cat ${PIDFILE}`
else
echo$ {NAME} is NOT running
exit 1
fi
;;
*)
echo "Usage: sudo $0 {start|stop|status|restart}"
echo ""
echo "start numPings - send numPing bursts to frame repeater, one every ${PAUSE} seconds, logfile is ${LOGFILE}"
echo "start_verbose - send single burst to frame repeater in foreground, no logfile"
echo "start_loopback - local loopback test, Tx/Rx a single burst of 3 frames"
echo "start_carrier - Send carrier from HackRF at same tx power as FSK signal"
esac
exit 0

View File

@ -1,10 +0,0 @@
dash_host=penetrator
freq=144500000
Fs=240000
Rb=10000
rx_freq=$(($freq-2*$Rb))
numTxPackets=1000
bitsPerPacket=100
numTxBits=$(( ${numTxPackets}*${Rb}/${bitsPerPacket} ))
passRxPackets=$(( $numTxPackets-10 ))
rx_secs=15

View File

@ -1,20 +0,0 @@
#!/bin/bash -x
#
# Send test frames from Pi, receive and check on rtlsdr, using integrated rtl_fsk
source test/include.sh
results=mktemp
# kick off rx in background
rtl-sdr-blog/build_rtlsdr/src/rtl_fsk -g 1 -s $Fs -f $rx_freq - -n $(($Fs*$rx_secs)) -u $dash_host | codec2/build_linux/src/fsk_put_test_bits -q -p $passRxPackets - 2>&1 | tee ${RESULTS} &
rx_pid=$!
# make sure rx is running
sleep 2
# send packets on Pi
ssh pi@raspberrypi "cd pirip/tx; ../../codec2/build_linux/src/fsk_get_test_bits - $numTxBits | sudo ./fsk_rpitx -"
# check results
wait ${rx_pid}

View File

@ -1,25 +0,0 @@
#!/bin/bash -x
#
# Send test frames from Pi, receive and check on rtlsdr, using vanilla rtl_sdr
Fs=240000
Rb=10000
numTxPackets=100
bitsPerPacket=100
numTxBits=$(( ${numTxPackets}*${Rb}/${bitsPerPacket} ))
passRxPackets=$(( $numTxPackets-10 ))
results=mktemp
# kick off rx in background
tsecs=5; rtl-sdr-blog/build_rtlsdr/src/rtl_sdr -g 1 -s $Fs -f 144500000 - -n $(($Fs*$tsecs)) | codec2/build_linux/src/fsk_demod -d -p 24 2 $Fs $Rb - - | codec2/build_linux/src/fsk_put_test_bits -q -p $passRxPackets - 2>&1 | tee ${RESULTS} &
rx_pid=$!
sleep 2
# send packets on Pi
ssh pi@raspberrypi "cd pirip/tx; ../../codec2/build_linux/src/fsk_get_test_bits - $numTxBits | sudo ./fsk_rpitx -"
# check results
wait ${rx_pid}

View File

@ -1,19 +0,0 @@
# Makefile for rpitx_fsk, a fsk modulator for the Pi
all: rpitx_fsk frame_repeater
CODEC2_DIR=$(HOME)/pirip/codec2/build_linux/src
LIBRTLSDR_DIR=$(HOME)/pirip/librpitx/src/
CXXFLAGS = -std=c++11 -Wall -g -O2 -Wno-unused-variable -I$(HOME)/pirip/codec2/src
CFLAGS = -Wall -g -O2 -Wno-unused-variable -I$(HOME)/pirip/codec2/src
LDFLAGS = $(LIBRTLSDR_DIR)/librpitx.a $(CODEC2_DIR)/libcodec2.so -Wl,-rpath=$(CODEC2_DIR) -lm -lrt -lpthread
CCP = c++
CC = cc
rpitx_fsk : rpitx_fsk.cpp $(LIBRTLSDR_DIR)/librpitx.a
$(CCP) $(CXXFLAGS) -o rpitx_fsk rpitx_fsk.cpp $(LDFLAGS)
frame_repeater : frame_repeater.c
$(CC) $(CFLAGS) -o $@ $^

View File

@ -1,120 +0,0 @@
/*
frame_repeater.c
David Rowe Nov 2020
Sits between rtl_fsk and rpitx_fsk, to implement a pirip frame
repeater, useful for unattended testing. Allows us to "ping" a
terminal.
rtl_fsk | frame_repeater | rpitx_fsk
+ State machine gets "clocked" every time rtl_fsk sends a status byte.
+ Bursts are accumulated until complete, then sent to rpitx_fsk for re-transmission
+ we wait for the estimated time of the Tx burst, ignoring any frames received during the Tx burst,
as our sensitive RX will be picking up what we are sending
TODO:
[X] Show it can echo one frame
[X] Show it can echo multiple frames
[X] leave running for a few hours with no lockups
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include "freedv_api_internal.h"
#define IDLE 0
#define RECEIVING_BURST 1
#define MAX_FRAMES 100
int main(int argc, char *argv[]) {
int state = IDLE;
int next_state, bytes_in, bytes_out, nframes, ret, nclocks;
uint8_t burst_control, source_byte;
time_t last_time = 0;
if (argc < 3) {
fprintf(stderr, "usage: %s dataBitsPerFrame sourceByte\n", argv[0]);
exit(1);
}
int data_bits_per_frame = atoi(argv[1]);
assert((data_bits_per_frame % 8) == 0);
int data_bytes_per_frame = data_bits_per_frame/8;
fprintf(stderr, "frame_repeater: %d %d\n", data_bits_per_frame, data_bytes_per_frame);
uint8_t data[data_bytes_per_frame];
uint8_t data_buffer[data_bytes_per_frame*MAX_FRAMES];
source_byte = strtol(argv[2], NULL, 0);
/* At regular intervals rtl_fsk sends a status byte then
data_bytes_per_frame. If there is no received data then
data_buffer will be all zeros. */
uint8_t rx_status, prev_rx_status = 0; int time_ms; nclocks = 0; int wait_until_ms;
while(fread(&rx_status, sizeof(uint8_t), 1, stdin)) {
ret = fread(data, sizeof(uint8_t), data_bytes_per_frame, stdin);
assert(ret == data_bytes_per_frame);
nclocks++;
/* update rate from rtl_fsk can be quite fast, so only fprintf status updates when the rx_status
changes */
next_state = state;
switch(state) {
case IDLE:
if (rx_status == (FREEDV_RX_SYNC | FREEDV_RX_BITS)) {
fprintf(stderr, "frame_repeater: Starting to receive a burst\n");
memcpy(data_buffer, data, data_bytes_per_frame);
bytes_in = data_bytes_per_frame;
nframes = 1;
next_state = RECEIVING_BURST;
}
break;
case RECEIVING_BURST:
if (rx_status & FREEDV_RX_BITS) {
fprintf(stderr, "frame_repeater: Receiving a frame in a burst\n");
// buffer latest packet
memcpy(&data_buffer[bytes_in], data, data_bytes_per_frame);
bytes_in += data_bytes_per_frame;
nframes++;
assert(bytes_in <= data_bytes_per_frame*MAX_FRAMES);
}
if (!(rx_status & FREEDV_RX_SYNC)) {
/* We've lost RX_SYNC so receive burst finished. So now we can Tx the frames we have buffered */
fprintf(stderr, "frame_repeater: Sending received %d frame burst of %d bytes\n", nframes, bytes_in);
bytes_out = 0; burst_control = 1;
while(bytes_out != bytes_in) {
fwrite(&burst_control, sizeof(uint8_t), 1, stdout);
/* change source address so our local receiver can filter these packets out */
data_buffer[bytes_out] = source_byte;
fwrite(&data_buffer[bytes_out], sizeof(uint8_t), data_bytes_per_frame, stdout);
bytes_out += data_bytes_per_frame;
burst_control = 0;
}
burst_control = 2; memset(data, 0, data_bytes_per_frame);
fwrite(&burst_control, sizeof(uint8_t), 1, stdout);
fwrite(data, sizeof(uint8_t), data_bytes_per_frame, stdout);
fflush(stdout);
next_state = IDLE;
}
break;
default:
assert(0);
}
/* just log output when state changes to avoid too much noise. Also log every 10s as a heartbeat, if that stops it means
rtl_fsk or this program has choked */
if ((state != next_state) || (rx_status & FREEDV_RX_BITS) || (time(NULL) > (last_time + 10))) {
fprintf(stderr, "frame_repeater: [%d] state %d next_state: %d rx_status: 0x%02x\n", nclocks, state, next_state, rx_status);
last_time = time(NULL);
}
state = next_state;
}
return 0;
}

View File

@ -1,561 +0,0 @@
/*
rpitx_fsk.cpp
David Rowe July 2020
FSK modulates an input bit stream using rpitx.
TODO:
[X] option for packed/unpacked byte input data
[ ] Fix operation at Rs<200, at present we can't detect any packets at Rs=100
[ ] test with other LDPC codes
+ high rate codes are of interest, big gains for small overhead
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <signal.h>
#include <getopt.h>
#include <unistd.h>
#include <fcntl.h>
#include "../librpitx/src/librpitx.h"
#include "ldpc_codes.h"
#include "freedv_api.h"
#define MAX_CHAR 256
#define FIFO_SIZE 100
// Functions to build FSK_LDPC frame. Not normally exposed by FreeDV API
extern "C" {
int freedv_tx_fsk_ldpc_bits_per_frame(struct freedv *f);
void freedv_tx_fsk_ldpc_framer(struct freedv *f, uint8_t frame[], uint8_t payload_data[]);
void ofdm_generate_payload_data_bits(uint8_t payload_data_bits[], int n);
unsigned short freedv_gen_crc16(unsigned char* data_p, int length);
void freedv_pack(unsigned char *bytes, unsigned char *bits, int nbits);
void freedv_unpack(unsigned char *bits, unsigned char *bytes, int nbits);
}
bool running=true;
static int terminate_calls = 0;
ngfmdmasync *fmmod = NULL;
float initial_frequency = 0.0;
// Signals like Ctrl-C or segfaults get handled here
static void terminate(int num) {
terminate_calls++;
running=false;
fprintf(stderr,"Caught signal %d - Terminating\n", num);
if (terminate_calls >= 5) {
fprintf(stderr, "Too many signals - finishing....\n");
fmmod->clkgpio::print_clock_tree();
// make sure TX is off if we have to abort
if (fmmod) delete fmmod;
exit(1);
}
}
/* Use the Linux /sys/class/gpio system to access the RPis GPIOs */
void sys_gpio(const char filename[], const char s[]) {
FILE *fgpio = fopen(filename, "wt");
//fprintf(stderr,"%s %s\n",filename, s);
if (fgpio == NULL) {
fprintf(stderr, "\nProblem opening %s\n", filename);
exit(1);
}
fprintf(fgpio,"%s",s);
fclose(fgpio);
}
// calculate and insert CRC in the last 16 bits of (unpacked) data_bits[]
void calculate_and_insert_crc(uint8_t data_bits[], int data_bits_per_frame) {
assert((data_bits_per_frame % 8) == 0);
int data_bytes_per_frame = data_bits_per_frame / 8;
uint8_t data_bytes[data_bytes_per_frame];
freedv_pack(data_bytes, data_bits, data_bits_per_frame-16);
uint16_t crc16 = freedv_gen_crc16(data_bytes, data_bytes_per_frame-2);
uint8_t crc16_bytes[] = { (uint8_t)(crc16 >> 8), (uint8_t)(crc16 & 0xff) };
freedv_unpack(data_bits+data_bits_per_frame-16, crc16_bytes, 16);
}
/* Sanity check PLLC. During development this was getting modified outside of rpitx.
Fix is to add "force_turbo=1" to the end of /boot/config.txt */
#define PLLC_CTRL (0x1120 / 4) // from libpitx/src/gpio.h
float initial_pllc_freq() {
volatile uint32_t *gpioreg = fmmod->clkgpio::gpioreg;
return 19.2 * 1E6 * ((float)(gpioreg[PLLC_CTRL] & 0x3ff) + ((float)gpioreg[PLLC_FRAC]) / ((float)(1 << 20)));
}
void check_pllc() {
volatile uint32_t *gpioreg = fmmod->clkgpio::gpioreg;
float current_freq = 19.2 * 1E6 * ((float)(gpioreg[PLLC_CTRL] & 0x3ff) + ((float)gpioreg[PLLC_FRAC]) / ((float)(1 << 20)));
float delta = fabs(current_freq - initial_frequency)/initial_frequency;
if (delta > 0.01) {
fprintf(stderr, "PLLC PDIV=%d NDIV=%d FRAC=%d ", (gpioreg[PLLC_CTRL] >> 12)&0x7, gpioreg[PLLC_CTRL] & 0x3ff, gpioreg[PLLC_FRAC]);
fprintf(stderr, " %f MHz\n", 19.2 * ((float)(gpioreg[PLLC_CTRL] & 0x3ff) + ((float)gpioreg[PLLC_FRAC]) / ((float)(1 << 20))));
fprintf(stderr, "\nPLLC getting modified outside of rpitx! Add \"force_turbo=1\" to the end of /boot/config.txt\n");
delete fmmod;
exit(1);
}
}
// My attempt at a function to send samples to the DMA circular
// buffer. Not sure if this is any better than ngfmdamsync functions!
// Interesting area for further work.
int SetFrequencySampleNonBlocking(ngfmdmasync *fmmod, float Frequency)
{
int Index = fmmod->GetUserMemIndex();
if (Index == -1) {
return -1;
} else {
fmmod->sampletab[Index]=(0x5A<<24)|fmmod->GetMasterFrac(Frequency);
fmmod->PushSample(Index);
return 0;
}
}
// FSK Modulate and Tx a frame of bits
void modulate_frame(ngfmdmasync *fmmod, float shiftHz, int m, uint8_t tx_frame[], int bits_per_frame) {
assert(fmmod != NULL);
for(int bit_i=0; bit_i<bits_per_frame;) {
/* generate the symbol number from the bit stream,
e.g. 0,1 for 2FSK, 0,1,2,3 for 4FSK */
int sym = 0;
for(int i=m; i>>=1; ) {
uint8_t bit = tx_frame[bit_i] & 0x1;
sym = (sym<<1)|bit;
bit_i++;
}
float VCOfreqHz = shiftHz*sym;
while (SetFrequencySampleNonBlocking(fmmod,VCOfreqHz)) usleep(100);
}
}
int main(int argc, char **argv)
{
for (int i = 0; i < 64; i++) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = terminate;
sigaction(i, &sa, NULL);
}
FILE *fin;
int carrier_test = 0;
int one_zero_test = 0;
float frequency = 144.5e6;
int SymbolRate = 10000;
int m = 2;
int log2m;
float shiftHz = -1;
struct freedv *freedv = NULL;
struct freedv_advanced adv;
int fsk_ldpc = 0;
int data_bits_per_frame; // number of payload data bits
int bits_per_frame; // total number of bits including UW, payload data, parity bits for FSK + LDPC mode
int testframes = 0;
int Nframes = 0;
int Nbursts = 1;
int sequence_numbers = 0;
char ant_switch_gpio[128] = "";
char ant_switch_gpio_path[MAX_CHAR] = "";
int rpitx_fsk_fifo = 0;
int packed = 0;
uint8_t source_byte = 0;
int inter_burst_delay_ms = 0;
char usage[] = "usage: %s [-m fskM 2|4] [-f carrierFreqHz] [-r symbRateHz] [-s shiftHz] [-t] [-c] "
"[--testframes Nframes] InputOneBitPerCharFile\n"
" -c Carrier test mode\n"
" -g GPIO that controls antenna Tx/Rx switch (1 for Tx, 0 for Rx)\n"
" -t ...0101010... FSK test mode\n"
" --code CodeName Use LDPC code CodeName\n"
" --listcodes List available LDPC codes\n"
" --testframes N Send N testframes per burst\n"
" --bursts B Send B bursts of N testframes (default 1)\n"
" --delay ms testframe inter-burst delay in ms (default 2 frames)\n"
" --seq send packet sequence numbers (breaks testframe BER counting) in byte[1]\n"
" --fifo fifoName send stats messages to fifoName\n"
" --packed packed byte input\n"
" --source Byte insert a (non-zero) source address att byte[0]\n"
"\n"
" Example 1, send 10000 bits of (100 bit) tests frames from external test frame generator\n"
" at 1000 bits/s using 2FSK:\n\n"
" $ ../codec2/build_linux/src/fsk_get_test_bits - 10000 | sudo ./rpitx_fsk - -r 1000 -s 1000\n\n"
" Example 2, send two LDPC encoded test frames at 1000 bits/s using 2FSK:\n\n"
" $ sudo ./rpitx_fsk /dev/zero --code H_256_512_4 -r 1000 -s 1000 --testframes 2\n";
int opt = 0;
int opt_idx = 0;
while( opt != -1 ){
static struct option long_opts[] = {
{"code", required_argument, 0, 'a'},
{"listcodes", no_argument, 0, 'b'},
{"testframes",required_argument, 0, 'u'},
{"bursts", required_argument, 0, 'e'},
{"seq", no_argument, 0, 'q'},
{"fifo", required_argument, 0, 'i'},
{"packed", no_argument, 0, 'l'},
{"source", required_argument, 0, 'd'},
{"delay", required_argument, 0, 'j'},
{0, 0, 0, 0}
};
opt = getopt_long(argc,argv,"a:bcd:e:f:g:i:m:qr:s:tu:",long_opts,&opt_idx);
switch (opt) {
case 'a':
fsk_ldpc = 1;
adv.codename = optarg;
break;
case 'b':
ldpc_codes_list();
exit(0);
break;
case 'c':
carrier_test = 1;
break;
case 'd':
source_byte = strtol(optarg, NULL, 0);
fprintf(stderr,"source byte: 0x%02x\n", source_byte);
break;
case 'e':
Nbursts = atoi(optarg);
break;
case 'm':
m = atoi(optarg);
break;
case 'l':
packed = 1;
break;
case 'f':
frequency = atof(optarg);
break;
case 'i':
rpitx_fsk_fifo = open(optarg, O_WRONLY);
if (rpitx_fsk_fifo == -1) {
fprintf(stderr, "Error opening fifo %s\n", argv[2]);
exit(1);
}
fprintf(stderr, "rpitx_fsk: FIFO opened OK ...\n");
break;
case 'q':
sequence_numbers = 1;
break;
case 'r':
SymbolRate = atof(optarg);
break;
case 's':
shiftHz = atof(optarg);
break;
case 't':
one_zero_test = 1;
break;
case 'u':
if (fsk_ldpc == 0) {
fprintf(stderr, "internal testframe mode only supported in --code coded mode!\n");
exit(1);
}
testframes = 1;
Nframes = atoi(optarg);
fprintf(stderr, "rpitx_fsk: Sending %d testframe(s)...\n", Nframes);
break;
case 'g':
strcpy(ant_switch_gpio, optarg);
sys_gpio("/sys/class/gpio/unexport", ant_switch_gpio);
sys_gpio("/sys/class/gpio/export", ant_switch_gpio);
usleep(100*1000); /* short delay so OS can create the next device */
char tmp[MAX_CHAR];
sprintf(tmp,"/sys/class/gpio/gpio%s/direction", ant_switch_gpio);
sys_gpio(tmp, "out");
sprintf(ant_switch_gpio_path,"/sys/class/gpio/gpio%s/value", ant_switch_gpio);
sys_gpio(ant_switch_gpio_path, "0");
break;
case 'j':
inter_burst_delay_ms = atoi(optarg);
break;
case 'h':
case '?':
fprintf(stderr, usage, argv[0]);
exit(1);
}
}
if (argc < 2) {
fprintf(stderr, usage, argv[0]);
exit(1);
}
fin = stdin;
if (!(carrier_test || one_zero_test)) {
if (strcmp(argv[optind],"-") != 0) {
fin = fopen(argv[optind],"rb");
if (fin == NULL) {
fprintf(stderr, "Error opening input file: %s\n", argv[optind]);
exit(1);
}
}
}
int npreamble_symbols = 50*(m>>1);
int npreamble_bits = npreamble_symbols*(m>>1);
uint8_t preamble_bits[npreamble_bits];
if (fsk_ldpc) {
// setup LDPC encoder and framer
adv.Rs = SymbolRate;
adv.Fs = 8*SymbolRate; // just required to satisfy freedv_open FSK_LDPC, as we don't run FSK demod here
adv.M = m;
freedv = freedv_open_advanced(FREEDV_MODE_FSK_LDPC, &adv);
assert(freedv != NULL);
data_bits_per_frame = freedv_get_bits_per_modem_frame(freedv);
bits_per_frame = freedv_tx_fsk_ldpc_bits_per_frame(freedv);
fprintf(stderr, "rpitx_fsk: FSK LDPC mode code: %s data_bits_per_frame: %d\n", adv.codename, data_bits_per_frame);
/* set up preamble */
/* TODO: this should be a freeDV API function */
// cycle through all 2 and 4FSK symbols, not sure if this is better than random
int sym = 0;
for(int i=0; i<npreamble_bits; i+=2) {
preamble_bits[i] = (sym>>1) & 0x1;
preamble_bits[i+1] = sym & 0x1;
sym += 1;
}
} else {
// uncoded mode
data_bits_per_frame = log2(m);
bits_per_frame = data_bits_per_frame;
}
/* Start RpiTX ---------------------------------------------------------------------------------*/
// Set shiftHz at 2*Rs if no command line argument
if (shiftHz == -1)
shiftHz = 2*SymbolRate;
dbg_setlevel(1);
fmmod = new ngfmdmasync(frequency,SymbolRate,14,FIFO_SIZE);
initial_frequency = initial_pllc_freq();
fmmod->clkgpio::disableclk(4);
fprintf(stderr, "rpitx_fsk: Frequency: %4.1f MHz Rs: %4.1f kHz Shift: %4.1f kHz M: %d \n", frequency/1E6, SymbolRate/1E3, shiftHz/1E3, m);
fprintf(stderr, "rpitx_fsk: data_bits_per_frame: %d bits_per_frame: %d\n", data_bits_per_frame, bits_per_frame);
if ((carrier_test == 0) && (one_zero_test == 0)) {
// FSK Tx --------------------------------------------------------------------
uint8_t data_bits[data_bits_per_frame];
uint8_t tx_frame[bits_per_frame];
if (testframes) {
/* FSK_LDPC Tx in test frame mode -------------------------------------------*/
assert(fsk_ldpc);
ofdm_generate_payload_data_bits(data_bits, data_bits_per_frame);
for(int b=0; b<Nbursts; b++) {
// antenna switch to Tx
if (*ant_switch_gpio_path) sys_gpio(ant_switch_gpio_path, "1");
// transmitter carrier on
fmmod->clkgpio::enableclk(4);
//fmmod = new ngfmdmasync(frequency,SymbolRate,14,FIFO_SIZE);
// send pre-amble at start of burst
modulate_frame(fmmod, shiftHz, m, preamble_bits, npreamble_bits);
for (int f=0; f<Nframes; f++) {
// optional injection of source address byte
if (source_byte) {
for (int i=0; i<8; i++)
data_bits[i] = (source_byte >> (7-i)) & 0x1;
}
// optional injection of sequence numbers to help locate bad frames
if (sequence_numbers) {
int seq = (f+1) & 0xff;
for (int i=0; i<8; i++)
data_bits[8+i] = (seq >> (7-i)) & 0x1;
}
calculate_and_insert_crc(data_bits, data_bits_per_frame);
freedv_tx_fsk_ldpc_framer(freedv, tx_frame, data_bits);
modulate_frame(fmmod, shiftHz, m, tx_frame, bits_per_frame);
// allow early exit on Crtl-C
if (!running) goto finished;
}
// wait for enough time for FIFO to empty
int bufferSamples = FIFO_SIZE - fmmod->GetBufferAvailable();
float tdelay = (float)bufferSamples/SymbolRate;
usleep((int)(tdelay*1E6));
fprintf(stderr, "rpitx_fsk: End of burst %d\n", b);
// transmitter carrier off between bursts
fmmod->clkgpio::disableclk(4);
//delete fmmod;
// antenna switch to Rx
if (*ant_switch_gpio_path) sys_gpio(ant_switch_gpio_path, "0");
if (inter_burst_delay_ms == 0) {
// Two frames delay so we have some interpacket silence
tdelay = (2.0/SymbolRate)*bits_per_frame/(m>>1);
} else {
tdelay = inter_burst_delay_ms/1E3;
}
usleep((int)(tdelay*1E6));
check_pllc();
}
}
else {
/* regular FSK or FSK_LDPC Tx operation with bits/bytes from stdin ----------------------*/
int nframes = 0;
while(1) {
uint8_t burst_control;
int nRead;
if (fsk_ldpc) {
// in fsk_ldpc mode we prepend input data with a burst control byte
nRead = fread(&burst_control, sizeof(uint8_t), 1, fin);
if (nRead == 0) goto finished;
}
if (packed) {
int data_bytes_per_frame = data_bits_per_frame/8;
uint8_t data_bytes[data_bytes_per_frame];
nRead = fread(data_bytes, sizeof(uint8_t), data_bytes_per_frame, fin);
freedv_unpack(data_bits, data_bytes, data_bits_per_frame);
nRead *= 8;
}
else {
nRead = fread(data_bits, sizeof(uint8_t), data_bits_per_frame, fin);
}
fprintf(stderr, "rpitx_fsk: burst_control: %d nRead: %d\n", burst_control, nRead);
if (nRead != data_bits_per_frame) goto finished;
if (fsk_ldpc) {
// start of burst
if (burst_control == 1) {
fprintf(stderr, "rpitx_fsk: Tx on\n");
// antenna switch to Tx
if (*ant_switch_gpio_path) sys_gpio(ant_switch_gpio_path, "1");
// transmitter carrier on
fmmod->clkgpio::enableclk(4);
//fmmod = new ngfmdmasync(frequency,SymbolRate,14,FIFO_SIZE);
// send preamble
fprintf(stderr, "rpitx_fsk: sending preamble\n");
modulate_frame(fmmod, shiftHz, m, preamble_bits, npreamble_bits);
nframes = 0;
}
if ((burst_control == 0) || (burst_control == 1)) {
fprintf(stderr, "rpitx_fsk: sending frame: %d\n", nframes); nframes++;
// send a data frame, note last two bytes in frame replaced with CRC
calculate_and_insert_crc(data_bits, data_bits_per_frame);
freedv_tx_fsk_ldpc_framer(freedv, tx_frame, data_bits);
modulate_frame(fmmod, shiftHz, m, tx_frame, bits_per_frame);
}
// end of burst - this has a dummy data frame so don't send
if (burst_control == 2) {
// wait for enough time for FIFO to empty
int bufferSamples = FIFO_SIZE - fmmod->GetBufferAvailable();
float tdelay = (float)bufferSamples/SymbolRate;
usleep((int)(tdelay*1E6));
// transmitter carrier off between bursts
fmmod->clkgpio::disableclk(4);
//delete fmmod;
// antenna switch to Rx
if (*ant_switch_gpio_path) sys_gpio(ant_switch_gpio_path, "0");
fprintf(stderr, "rpitx_fsk: Tx off\n");
if (rpitx_fsk_fifo) {
char buf[256];
sprintf(buf, "Tx off");
if (write(rpitx_fsk_fifo, buf, strlen(buf)+1) ==-1) {
fprintf(stderr, "rpitx_fsk: error writing to FIFO\n");
}
}
}
}
else {
// uncoded mode - just send data_bits without any further framing
memcpy(tx_frame, data_bits, data_bits_per_frame);
modulate_frame(fmmod, shiftHz, m, tx_frame, bits_per_frame);
}
// allow us to bail on Ctrl-C
if (!running) goto finished;
check_pllc();
}
}
}
if (carrier_test) {
fprintf(stderr, "Carrier test mode 1 sec on/off , Ctrl-C to exit\n");
int count = 0;
int cycles = 0;
float VCOfreqHz = 0;
fmmod->clkgpio::enableclk(4);
while(running) {
//fmmod->SetFrequencySamples(&VCOfreqHz,1);
while (SetFrequencySampleNonBlocking(fmmod, VCOfreqHz)) usleep(100);
count++;
if (count == SymbolRate)
fmmod->clkgpio::disableclk(4);
if (count == 2*SymbolRate) {
fmmod->clkgpio::enableclk(4);
count = 0;
fprintf(stderr,"\rcycles: %d", ++cycles);
}
}
}
if (one_zero_test) {
fprintf(stderr, "...010101... test mode, Ctrl-C to exit\n");
float VCOfreqHz = 0;
fmmod->clkgpio::enableclk(4);
while(running) {
if (VCOfreqHz == shiftHz)
VCOfreqHz = 0;
else
VCOfreqHz = shiftHz;
while (SetFrequencySampleNonBlocking(fmmod, VCOfreqHz)) usleep(100);
}
}
finished:
check_pllc();
fprintf(stderr, "finishing....\n");
if (fmmod) delete fmmod;
if (fsk_ldpc) freedv_close(freedv);
if (*ant_switch_gpio) {
sys_gpio(ant_switch_gpio_path, "0");
sys_gpio("/sys/class/gpio/unexport", ant_switch_gpio);
}
return 0;
}