mirror of https://github.com/drowe67/pirip.git
commit
b301a526c9
51
README.md
51
README.md
|
@ -16,7 +16,7 @@ Currently working on M4 -Over The Air (OTA) tests.
|
|||
| M4 | ~~first OTA tests using uncoded modem~~ | repeat after recent tuning - demo an OTA link with margin |
|
||||
| M5 | ~~Pi running Tx and Rx~~ | Half duplex, loopback demo would be neat |
|
||||
| M6 | ~~Add LDPC FEC to waveform~~ | Needs to be tested/tuned OTA |
|
||||
| M7 | Bidirectional half duplex Tx/Rx on single Pi | For example two Pis ping-ponging frames at each other |
|
||||
| M7 | ~~Bidirectional half duplex Tx/Rx on single Pi~~ | frame repeater (ping) application developed and tested on the bench |
|
||||
| M8 | TAP/TUN integration and demo IP link | What protocol? |
|
||||
| M9 | Document how to build simple wire antennas | |
|
||||
|
||||
|
@ -50,11 +50,11 @@ $ ./build_rtlsdr.sh
|
|||
|
||||
1. Transmit two tone test signal for Pi:
|
||||
```
|
||||
pi@raspberrypi:~/pirip/tx $ sudo ./fsk_rpitx -t /dev/null
|
||||
pi@raspberrypi:~/pirip/tx $ sudo ./rpitx_fsk -t /dev/null
|
||||
```
|
||||
1. Transmit test frames from Pi for 60 seconds:
|
||||
```
|
||||
pi@raspberrypi:~/pirip/tx $ ../codec2/build_linux/src/fsk_get_test_bits - 600000 | sudo ./fsk_rpitx -
|
||||
pi@raspberrypi:~/pirip/tx $ ../codec2/build_linux/src/fsk_get_test_bits - 600000 | sudo ./rpitx_fsk -
|
||||
```
|
||||
1. Receive test frames on x86 laptop for 5 seconds (vanilla rtl_sdr):
|
||||
```
|
||||
|
@ -132,7 +132,7 @@ $ ./build_rtlsdr.sh
|
|||
1. FSK with LDPC and framer at 1000 bit/s. On the Pi Tx, we use an external source of test frames:
|
||||
```
|
||||
$ cd ~/pirip/tx
|
||||
$ ../codec2/build_linux/src/fsk_get_test_bits - 2560 256 | sudo ./fsk_rpitx - --code H_256_512_4 -r 1000 -s 1000
|
||||
$ ../codec2/build_linux/src/fsk_get_test_bits - 2560 256 | sudo ./rpitx_fsk - --code H_256_512_4 -r 1000 -s 1000
|
||||
```
|
||||
Laptop Rx:
|
||||
```
|
||||
|
@ -144,7 +144,7 @@ $ ./build_rtlsdr.sh
|
|||
|
||||
1. FSK with LDPC and framer at 10000 bit/s, internal test frames. On the Pi Tx:
|
||||
```
|
||||
$ sudo ./fsk_rpitx /dev/zero --code H_256_512_4 -r 10000 -s 10000 --testframes 10
|
||||
$ sudo ./rpitx_fsk /dev/zero --code H_256_512_4 -r 10000 -s 10000 --testframes 10
|
||||
```
|
||||
Laptop Rx:
|
||||
```
|
||||
|
@ -223,13 +223,52 @@ $ ./build_rtlsdr.sh
|
|||
the moment.
|
||||
1. Still, it's very nice to see 10 kbit/s moving through the system. A nice milestone.
|
||||
|
||||
1. Burst control - using a GPIO to control an antenna Tx/Rx pin diode switch on GPIO 21 (4FSK at 10k symbs/s):
|
||||
|
||||
1. Pi Tx with built in test frame:
|
||||
```
|
||||
sudo ./rpitx_fsk /dev/zero --code H_256_512_4 -r 10000 -s 10000 --testframes 10 --bursts 10 --seq -g 21 -m 4
|
||||
```
|
||||
1. Or Pi Tx using external source of frames:
|
||||
```
|
||||
../codec2/build_linux/src/ofdm_get_test_bits --length 256 --bcb --frames 2 | sudo ./rpitx_fsk - --code H_256_512_4 -r 10000 -s 10000 --seq -g 21 -m 4
|
||||
```
|
||||
An extra "burst control byte" is pre-pended to each frame of 256 data bits, that tells the Tx to start and stop a
|
||||
burst. At the start of a burst the antenna switch GPIO is set to "Tx", and we start our FSK Tx carrier. At the
|
||||
end of a burst we shut down the FSK Tx carrier, and set the antenna switch GPIO to Rx.
|
||||
|
||||
Rx on laptop:
|
||||
```
|
||||
./src/rtl_fsk -g 30 -f 144490000 - -r 10000 -m 2 -a 180000 --code H_256_512_4 -v -u localhost --testframes -m 4 --mask 10000 > /dev/null
|
||||
```
|
||||
|
||||
1. Frame Repeater, 10000 bits, 2FSK. Start up repeater on Pi.
|
||||
```
|
||||
$ cd pirip/lirtlsdr/build_linux
|
||||
$ ./src/rtl_fsk -g 49 -f 144490000 - -a 200000 -r 10000 --code H_256_512_4 --mask 10000 --filter 0x2 -q | ~/pirip/tx/frame_repeater 256 0x2 | sudo ~/pirip/tx/rpitx_fsk - --code H_256_512_4 -r 10000 -s 10000 -g 21 --packed
|
||||
```
|
||||
Start up receiver on another machine (e.g. laptop):
|
||||
```
|
||||
$ cd ~/pirip/librtlsdr/build_rtlsdr
|
||||
$ ./src/rtl_fsk -g 49 -f 144490000 - -a 200000 -r 10000 --code H_256_512_4 --mask 10000 --filter 0x1 | hexdump
|
||||
```
|
||||
Then send test frames from HackRF on laptop, e.g. bursts of 3 frames:
|
||||
```
|
||||
$ ./src/freedv_data_raw_tx --source 0x1 -c --testframes 3 --burst 1 --Fs 100000 --Rs 10000 --tone1 10000 --shift 10000 -a 30000 FSK_LDPC /dev/zero - | ./misc/tlininterp - - 40 -d -f | hackrf_transfer -t - -s 4E6 -f 143.5E6
|
||||
```
|
||||
This uses a source addressing scheme to filter out locally transmitted frames. In the example above, the laptop has
|
||||
address 0x1, and the Pi addess 0x2. We tell the rtl_fsk Rx 0x1 to ignore any packets sent from 0x1. This neatly prevents the frame repeater from hearing it's own packets and going into a loop.
|
||||
|
||||

|
||||
|
||||
# Reading Further
|
||||
|
||||
1. [Open IP over VHF/UHF 1](http://www.rowetel.com/?p=7207) - Blog post introducing this project
|
||||
1. [Open IP over VHF/UHF 2](http://www.rowetel.com/?p=7334) - Second blog post on uncoded OTA tests
|
||||
1. [FSK_LDPC Data Mode](http://www.rowetel.com/?p=7467) - Physical layer design and testing
|
||||
1. [Codec 2 FSK Raw Data Modes](https://github.com/drowe67/codec2/blob/master/README_data.md)
|
||||
1. [Codec 2 FSK Modem](https://github.com/drowe67/codec2/blob/master/README_fsk.md)
|
||||
1. [Previous Codec 2 PR discussing this project](https://github.com/drowe67/codec2/pull/125)
|
||||
1. [RpiTx](https://github.com/F5OEO/rpitx) - Radio transmitter software for Raspberry Pis
|
||||
1. [rtlsdr driver](https://github.com/librtlsdr/librtlsdr) - Our rtlsdr driver is forked from this fine repo.
|
||||
1. [Measuring SDR Noise Figure in Real Time](http://www.rowetel.com/?page_id=6172)
|
||||
1. [Measuring SDR Noise Figure in Real Time](http://www.rowetel.com/?page_id=6172)
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 130 KiB |
20
tx/Makefile
20
tx/Makefile
|
@ -1,13 +1,19 @@
|
|||
# Makefile for fsk_rpitx, a fsk modulator for the Pi
|
||||
# Makefile for rpitx_fsk, a fsk modulator for the Pi
|
||||
|
||||
all: fsk_rpitx
|
||||
all: rpitx_fsk frame_repeater
|
||||
|
||||
CODEC2_DIR=../codec2/build_linux/src
|
||||
CXXFLAGS = -std=c++11 -Wall -g -O2 -Wno-unused-variable -I../codec2/src
|
||||
LDFLAGS = ../librpitx/src/librpitx.a $(CODEC2_DIR)/libcodec2.so -Wl,-rpath=$(CODEC2_DIR) -lm -lrt -lpthread
|
||||
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
|
||||
|
||||
fsk_rpitx : fsk_rpitx.cpp ../librpitx/src/librpitx.a
|
||||
$(CCP) $(CXXFLAGS) -o fsk_rpitx fsk_rpitx.cpp $(LDFLAGS)
|
||||
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 $@ $^
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
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:
|
||||
[ ] Show it can echo one frame
|
||||
[ ] Show it can echo multiple frames
|
||||
[ ] 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 "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;
|
||||
|
||||
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 ouput when state changes to avoid to omuch noise */
|
||||
if ((state != next_state) || (rx_status & FREEDV_RX_BITS))
|
||||
fprintf(stderr, "frame_repeater: [%d] state %d next_state: %d rx_status: 0x%02x\n", nclocks, state, next_state, rx_status);
|
||||
state = next_state;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
287
tx/fsk_rpitx.cpp
287
tx/fsk_rpitx.cpp
|
@ -1,287 +0,0 @@
|
|||
/*
|
||||
fsk_rpitx.cpp
|
||||
David Rowe July 2020
|
||||
|
||||
FSK modulates an input bit stream using rpitx.
|
||||
|
||||
TODO:
|
||||
[ ] decide if we should have packet bytes input data
|
||||
[ ] if so, need a way to send uncoded test packets as per examples in README.md
|
||||
[ ] Something wrong at Rs<200, e.g. Rx can't detect any packets at Rs=100
|
||||
[ ] test link with other codes
|
||||
*/
|
||||
|
||||
#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 "../librpitx/src/librpitx.h"
|
||||
#include "ldpc_codes.h"
|
||||
#include "freedv_api.h"
|
||||
|
||||
// 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;
|
||||
|
||||
static void terminate(int num) {
|
||||
terminate_calls++;
|
||||
running=false;
|
||||
fprintf(stderr,"Caught signal %d - Terminating\n", num);
|
||||
if (terminate_calls >= 5) exit(1);
|
||||
}
|
||||
|
||||
|
||||
void modulate_frame(ngfmdmasync *fmmod, float shiftHz, int m, uint8_t tx_frame[], int bits_per_frame) {
|
||||
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;
|
||||
fmmod->SetFrequencySamples(&VCOfreqHz,1);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
ngfmdmasync *fmmod;
|
||||
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 frames = 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"
|
||||
" -t ...0101010... FSK test mode\n"
|
||||
" --code CodeName Use LDPC code CodeName\n"
|
||||
" --listcodes List available LDPC codes\n"
|
||||
" --testframes built in testframes\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 ./fsk_rpitx - -r 1000 -s 1000\n\n"
|
||||
" Example 2, send two LDPC encoded test frames at 1000 bits/s using 2FSK:\n\n"
|
||||
" $ sudo ./fsk_rpitx /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'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
opt = getopt_long(argc,argv,"a:bm:f:r:s:tcu:",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 'm':
|
||||
m = atoi(optarg);
|
||||
break;
|
||||
case 'f':
|
||||
frequency = atof(optarg);
|
||||
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, "Sending %d testframes...\n", Nframes);
|
||||
break;
|
||||
case 'h':
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, "FSK LDPC mode code: %s data_bits_per_frame: %d\n", adv.codename, data_bits_per_frame);
|
||||
} else {
|
||||
// uncoded mode
|
||||
data_bits_per_frame = log2(m);
|
||||
bits_per_frame = data_bits_per_frame;
|
||||
}
|
||||
|
||||
// Set shiftHz at 2*Rs if no command line argument
|
||||
if (shiftHz == -1)
|
||||
shiftHz = 2*SymbolRate;
|
||||
fmmod = new ngfmdmasync(frequency,SymbolRate,14,100);
|
||||
padgpio pad;
|
||||
pad.setlevel(7); // Set max power
|
||||
|
||||
fprintf(stderr, "Frequency: %4.1f MHz Rs: %4.1f kHz Shift: %4.1f kHz M: %d \n", frequency/1E6, SymbolRate/1E3, shiftHz/1E3, m);
|
||||
|
||||
fprintf(stderr, "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)) {
|
||||
/* regular FSK modulator operation */
|
||||
|
||||
/* pre-amble TODO: reconcile this with same code in freedv_fsk.c */
|
||||
int npreamble_symbols = 50*(m>>1);
|
||||
int npreamble_bits = npreamble_symbols*(m>>1);
|
||||
uint8_t preamble_bits[npreamble_bits];
|
||||
// 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;
|
||||
}
|
||||
modulate_frame(fmmod, shiftHz, m, preamble_bits, npreamble_bits);
|
||||
|
||||
while(running) {
|
||||
uint8_t data_bits[data_bits_per_frame];
|
||||
int BytesRead = fread(data_bits, sizeof(uint8_t), data_bits_per_frame, fin);
|
||||
if (BytesRead == (data_bits_per_frame)) {
|
||||
if (testframes) {
|
||||
/* replace input data with testframe */
|
||||
ofdm_generate_payload_data_bits(data_bits, data_bits_per_frame);
|
||||
}
|
||||
|
||||
/* calculate and insert CRC in the last 16 bit sof data_bits[] */
|
||||
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);
|
||||
uint8_t tx_frame[bits_per_frame];
|
||||
if (fsk_ldpc)
|
||||
freedv_tx_fsk_ldpc_framer(freedv, tx_frame, data_bits);
|
||||
else
|
||||
memcpy(tx_frame, data_bits, data_bits_per_frame);
|
||||
modulate_frame(fmmod, shiftHz, m, tx_frame, bits_per_frame);
|
||||
}
|
||||
else
|
||||
running=false;
|
||||
frames++;
|
||||
if (testframes)
|
||||
if (frames >= Nframes) running = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (carrier_test) {
|
||||
fprintf(stderr, "Carrier test mode, Ctrl-C to exit\n");
|
||||
float VCOfreqHz = 0;
|
||||
while(running) {
|
||||
fmmod->SetFrequencySamples(&VCOfreqHz,1);
|
||||
}
|
||||
}
|
||||
|
||||
if (one_zero_test) {
|
||||
fprintf(stderr, "...010101... test mode, Ctrl-C to exit\n");
|
||||
float VCOfreqHz = 0;
|
||||
while(running) {
|
||||
if (VCOfreqHz == shiftHz)
|
||||
VCOfreqHz = 0;
|
||||
else
|
||||
VCOfreqHz = shiftHz;
|
||||
fmmod->SetFrequencySamples(&VCOfreqHz,1);
|
||||
}
|
||||
}
|
||||
|
||||
// this was required to prevent errors on final frame, I suspect
|
||||
// as fmmod FIFO hasn't emptied by the time we delete fmmod.
|
||||
// Seems a bit wasteful, so there might be a better way
|
||||
|
||||
float VCOfreqHz = 0;
|
||||
for(int i=0; i<50; i++)
|
||||
fmmod->SetFrequencySamples(&VCOfreqHz,1);
|
||||
printf("End of Tx\n");
|
||||
|
||||
if (fsk_ldpc) freedv_close(freedv);
|
||||
delete fmmod;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,492 @@
|
|||
/*
|
||||
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;
|
||||
|
||||
static void terminate(int num) {
|
||||
terminate_calls++;
|
||||
running=false;
|
||||
fprintf(stderr,"Caught signal %d - Terminating\n", num);
|
||||
if (terminate_calls >= 5) 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);
|
||||
}
|
||||
|
||||
void modulate_frame(ngfmdmasync *fmmod, float shiftHz, int m, uint8_t tx_frame[], int bits_per_frame) {
|
||||
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;
|
||||
fmmod->SetFrequencySamples(&VCOfreqHz,1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
ngfmdmasync *fmmod;
|
||||
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;
|
||||
|
||||
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"
|
||||
" --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 ./fsk_rpitx - -r 1000 -s 1000\n\n"
|
||||
" Example 2, send two LDPC encoded test frames at 1000 bits/s using 2FSK:\n\n"
|
||||
" $ sudo ./fsk_rpitx /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'},
|
||||
{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, "Sending %d testframes...\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 '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, "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;
|
||||
}
|
||||
|
||||
// Set shiftHz at 2*Rs if no command line argument
|
||||
if (shiftHz == -1)
|
||||
shiftHz = 2*SymbolRate;
|
||||
fmmod = new ngfmdmasync(frequency,SymbolRate,14,FIFO_SIZE);
|
||||
fmmod->clkgpio::disableclk(4);
|
||||
|
||||
fprintf(stderr, "Frequency: %4.1f MHz Rs: %4.1f kHz Shift: %4.1f kHz M: %d \n", frequency/1E6, SymbolRate/1E3, shiftHz/1E3, m);
|
||||
fprintf(stderr, "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);
|
||||
|
||||
// antenna switch to Tx
|
||||
if (*ant_switch_gpio_path) sys_gpio(ant_switch_gpio_path, "1");
|
||||
|
||||
for(int b=0; b<Nbursts; b++) {
|
||||
|
||||
// transmitter carrier on
|
||||
fmmod->clkgpio::enableclk(4);
|
||||
|
||||
// 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));
|
||||
|
||||
printf("End of this burst\n");
|
||||
|
||||
// transmitter carrier off between bursts
|
||||
fmmod->clkgpio::disableclk(4);
|
||||
|
||||
// Two frames delay so we have some interpacket silence
|
||||
tdelay = (2.0/SymbolRate)*bits_per_frame/(m>>1);
|
||||
usleep((int)(tdelay*1E6));
|
||||
}
|
||||
|
||||
// antenna switch to Rx
|
||||
if (*ant_switch_gpio_path) sys_gpio(ant_switch_gpio_path, "0");
|
||||
}
|
||||
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);
|
||||
// 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);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (carrier_test) {
|
||||
fprintf(stderr, "Carrier test mode 1 sec on/off , Ctrl-C to exit\n");
|
||||
int count = 0;
|
||||
float VCOfreqHz = 0;
|
||||
while(running) {
|
||||
fmmod->SetFrequencySamples(&VCOfreqHz,1);
|
||||
count++;
|
||||
if (count == SymbolRate)
|
||||
fmmod->clkgpio::disableclk(4);
|
||||
if (count == 2*SymbolRate) {
|
||||
fmmod->clkgpio::enableclk(4);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (one_zero_test) {
|
||||
fprintf(stderr, "...010101... test mode, Ctrl-C to exit\n");
|
||||
float VCOfreqHz = 0;
|
||||
while(running) {
|
||||
if (VCOfreqHz == shiftHz)
|
||||
VCOfreqHz = 0;
|
||||
else
|
||||
VCOfreqHz = shiftHz;
|
||||
fmmod->SetFrequencySamples(&VCOfreqHz,1);
|
||||
}
|
||||
}
|
||||
|
||||
finished:
|
||||
|
||||
// this was required to prevent errors on final frame, I suspect
|
||||
// as fmmod FIFO hasn't emptied by the time we delete fmmod.
|
||||
// Seems a bit wasteful, so there might be a better way
|
||||
|
||||
float VCOfreqHz = 0;
|
||||
for(int i=0; i<50; i++)
|
||||
fmmod->SetFrequencySamples(&VCOfreqHz,1);
|
||||
printf("End of Tx\n");
|
||||
|
||||
if (fsk_ldpc) freedv_close(freedv);
|
||||
delete fmmod;
|
||||
|
||||
if (*ant_switch_gpio) {
|
||||
sys_gpio(ant_switch_gpio_path, "0");
|
||||
sys_gpio("/sys/class/gpio/unexport", ant_switch_gpio);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue