├── po
├── LINGUAS
├── POTFILES.in
├── Makevars
├── ofdm-transfer.pot
└── fr.po
├── autogen.sh
├── Makefile.am
├── src
├── Makefile.am
├── ofdm-transfer.h
├── gettext.h
├── main.c
└── ofdm-transfer.c
├── examples
├── Makefile.am
├── full-duplex-ppp.sh
├── half-duplex.sh
├── full-duplex.c
└── echo-server.c
├── manifest.scm
├── tests
├── Makefile.am
├── test-program.sh
├── test-library-file.c
└── test-library-callback.c
├── .gitignore
├── configure.ac
├── README
└── LICENSE
/po/LINGUAS:
--------------------------------------------------------------------------------
1 | # Set of available languages.
2 | fr
3 |
--------------------------------------------------------------------------------
/po/POTFILES.in:
--------------------------------------------------------------------------------
1 | # List of source files which contain translatable strings.
2 | src/main.c
3 | src/ofdm-transfer.c
4 |
--------------------------------------------------------------------------------
/autogen.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 | SRCDIR="$(dirname $0)"
5 | cd "${SRCDIR}"
6 | autoreconf --install --force
7 |
--------------------------------------------------------------------------------
/Makefile.am:
--------------------------------------------------------------------------------
1 | AUTOMAKE_OPTIONS = foreign dist-lzip no-dist-gzip subdir-objects
2 | ACLOCAL_AMFLAGS = -I m4
3 |
4 | SUBDIRS = src examples po tests
5 |
6 | include_HEADERS = src/ofdm-transfer.h
7 | dist_doc_DATA = LICENSE README
8 | EXTRA_DIST = \
9 | examples/full-duplex-ppp.sh \
10 | examples/half-duplex.sh \
11 | tests/test-program.sh
12 |
--------------------------------------------------------------------------------
/src/Makefile.am:
--------------------------------------------------------------------------------
1 | lib_LTLIBRARIES = libofdm-transfer.la
2 | libofdm_transfer_la_SOURCES = gettext.h ofdm-transfer.c ofdm-transfer.h
3 | libofdm_transfer_la_LDFLAGS = -version-info 1:0:0
4 |
5 | bin_PROGRAMS = ofdm-transfer
6 | ofdm_transfer_SOURCES = gettext.h main.c ofdm-transfer.h
7 | ofdm_transfer_CFLAGS = -DLOCALEDIR=\"$(localedir)\"
8 | ofdm_transfer_LDADD = libofdm-transfer.la
9 |
--------------------------------------------------------------------------------
/examples/Makefile.am:
--------------------------------------------------------------------------------
1 | examplesdir =
2 | examples_PROGRAMS = full-duplex echo-server
3 | full_duplex_SOURCES = full-duplex.c
4 | full_duplex_CFLAGS = -I $(top_srcdir)/src
5 | full_duplex_LDADD = $(top_builddir)/src/libofdm-transfer.la -lpthread
6 | echo_server_SOURCES = echo-server.c
7 | echo_server_CFLAGS = -I $(top_srcdir)/src
8 | echo_server_LDADD = $(top_builddir)/src/libofdm-transfer.la
9 |
--------------------------------------------------------------------------------
/manifest.scm:
--------------------------------------------------------------------------------
1 | ;;; GNU Guix manifest to set the development environment
2 | ;;; guix shell -m manifest.scm
3 |
4 | (specifications->manifest
5 | '(;; Compiler and tools
6 | "autoconf"
7 | "automake"
8 | "coreutils"
9 | "diffutils"
10 | "findutils"
11 | "gawk"
12 | "gcc-toolchain"
13 | "gettext"
14 | "git"
15 | "grep"
16 | "libtool"
17 | "lzip"
18 | "make"
19 | "sed"
20 | "tar"
21 |
22 | ;; Libraries
23 | "liquid-dsp"
24 | "soapysdr"))
25 |
--------------------------------------------------------------------------------
/tests/Makefile.am:
--------------------------------------------------------------------------------
1 | check_PROGRAMS = test-library-callback test-library-file
2 | test_library_callback_SOURCES = test-library-callback.c
3 | test_library_callback_CFLAGS = -I $(top_srcdir)/src
4 | test_library_callback_LDADD = $(top_builddir)/src/libofdm-transfer.la
5 | test_library_file_SOURCES = test-library-file.c
6 | test_library_file_CFLAGS = -I $(top_srcdir)/src
7 | test_library_file_LDADD = $(top_builddir)/src/libofdm-transfer.la
8 | TESTS = test-library-callback test-library-file test-program.sh
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ABOUT-NLS
2 | aclocal.m4
3 | autom4te.cache/
4 | compile
5 | config.*
6 | configure
7 | depcomp
8 | .deps/
9 | .dirstamp
10 | examples/full-duplex
11 | examples/echo-server
12 | install-sh
13 | .libs/
14 | libtool
15 | ltmain.sh
16 | m4/
17 | Makefile
18 | Makefile.in
19 | missing
20 | po/*.gmo
21 | po/*.mo
22 | po/boldquot.sed
23 | po/en@boldquot.header
24 | po/en@quot.header
25 | po/insert-header.sin
26 | po/Makefile.in.in
27 | po/Makevars.template
28 | po/POTFILES
29 | po/quot.sed
30 | po/remove-potcdate.sin
31 | po/remove-potcdate.sed
32 | po/Rules-quot
33 | po/stamp-po
34 | src/ofdm-transfer
35 | test-driver
36 | test-suite.log
37 | tests/test-library-file
38 | tests/test-library-file.log
39 | tests/test-library-file.trs
40 | tests/test-library-callback
41 | tests/test-library-callback.log
42 | tests/test-library-callback.trs
43 | tests/test-program.sh.log
44 | tests/test-program.sh.trs
45 | *.la
46 | *.lo
47 | *.o
48 | *~
49 |
--------------------------------------------------------------------------------
/examples/full-duplex-ppp.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # This script makes a PPP connection between two machines using the
4 | # full-duplex example program.
5 | #
6 | # Copyright 2022 Guillaume LE VAILLANT
7 | #
8 | # This program is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | case "$1" in
22 | server)
23 | pppd noauth \
24 | local \
25 | lock \
26 | nodefaultroute \
27 | debug \
28 | nodetach \
29 | passive \
30 | 10.0.0.1:10.0.0.2 \
31 | pty "./full-duplex 433800000 434200000"
32 | ;;
33 |
34 | client)
35 | pppd noauth \
36 | local \
37 | lock \
38 | nodefaultroute \
39 | debug \
40 | nodetach \
41 | pty "./full-duplex 434200000 433800000"
42 | ;;
43 |
44 | *)
45 | echo "" >& 2
46 | echo "Usage: $0 " >& 2
47 | echo "" >& 2
48 | exit 1
49 | ;;
50 | esac
51 |
--------------------------------------------------------------------------------
/configure.ac:
--------------------------------------------------------------------------------
1 | AC_INIT(ofdm-transfer, 1.8.0)
2 | AM_INIT_AUTOMAKE
3 | AC_CONFIG_SRCDIR(src/ofdm-transfer.c)
4 | AC_CONFIG_MACRO_DIRS([m4])
5 |
6 | dnl Check for toolchain and install components
7 | AC_PROG_CC
8 | AC_PROG_INSTALL
9 | LT_INIT([shared disable-static])
10 |
11 | dnl Check for translation tools
12 | AM_GNU_GETTEXT([external])
13 | AM_GNU_GETTEXT_VERSION([0.19.1])
14 | AM_GNU_GETTEXT_REQUIRE_VERSION([0.19.1])
15 |
16 | dnl Check for standard headers
17 | AC_CHECK_HEADERS([complex.h fcntl.h locale.h signal.h stdio.h stdlib.h string.h strings.h unistd.h])
18 |
19 | dnl Check for functions
20 | AC_CHECK_FUNCS([fcntl])
21 | AC_CHECK_FUNCS([bindtextdomain setlocale textdomain])
22 | AC_CHECK_FUNCS([signal])
23 | AC_CHECK_FUNCS([fclose feof fflush fopen fprintf fread fwrite printf])
24 | AC_CHECK_FUNCS([exit free malloc strtof strtol strtoul])
25 | AC_CHECK_FUNCS([bzero memcmp memcpy strcasecmp strchr strcpy strlen strncasecmp])
26 | AC_CHECK_FUNCS([getopt usleep])
27 |
28 | dnl Check for libraries
29 | AC_CHECK_HEADERS(math.h, [], AC_MSG_ERROR([math headers required]))
30 | AC_CHECK_LIB(m, ceilf, [], AC_MSG_ERROR([math library required]))
31 |
32 | AC_CHECK_HEADERS(liquid/liquid.h, [], AC_MSG_ERROR([liquid-dsp header required]))
33 | AC_CHECK_LIB(liquid, ofdmflexframegen_create, [], AC_MSG_ERROR([liquid-dsp library required]))
34 |
35 | AC_CHECK_HEADERS(SoapySDR/Device.h, [], AC_MSG_ERROR([SoapySDR header required]))
36 | AC_CHECK_LIB(SoapySDR, SoapySDRDevice_make, [], AC_MSG_ERROR([SoapySDR library required]))
37 |
38 | AC_CHECK_HEADERS(pthread.h, [], AC_MSG_ERROR([pthread headers required]))
39 | AC_CHECK_LIB(pthread, pthread_create, [], AC_MSG_ERROR([pthread library required]))
40 |
41 | AC_CONFIG_FILES(Makefile examples/Makefile po/Makefile.in src/Makefile tests/Makefile)
42 | AC_OUTPUT
43 |
--------------------------------------------------------------------------------
/examples/half-duplex.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # This script makes a half-duplex connection between two machines
4 | # using audio, like a sound modem TNC.
5 | #
6 | # Copyright 2022 Guillaume LE VAILLANT
7 | #
8 | # This program is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | SAMPLE_RATE=48000
22 | FREQUENCY=1500
23 | BIT_RATE=1200
24 | MODULATION="bpsk"
25 |
26 | # Parameters for an Icom IC-705 radio connected via USB
27 | PCM_IN="alsa_input.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo"
28 | PCM_OUT="alsa_output.usb-Burr-Brown_from_TI_USB_Audio_CODEC-00.analog-stereo"
29 | AUDIO_GAIN=-35
30 | DEVICE="/dev/ttyACM0"
31 | MODEL=3085
32 | USE_PTT=true
33 |
34 | # Parameters for a Yaesu FT-818 radio connected via a Signalink USB interface
35 | # PCM_IN="alsa_input.usb-BurrBrown_from_Texas_Instruments_USB_AUDIO_CODEC-00.analog-stereo"
36 | # PCM_OUT="alsa_output.usb-BurrBrown_from_Texas_Instruments_USB_AUDIO_CODEC-00.analog-stereo"
37 | # AUDIO_GAIN=-6
38 | # USE_PTT=false
39 |
40 | # Parameters for a regular sound card
41 | # PCM_IN="alsa_input.pci-0000_17_00.4.analog-stereo"
42 | # PCM_OUT="alsa_output.pci-0000_17_00.4.analog-stereo"
43 | # AUDIO_GAIN=-10
44 | # USE_PTT=false
45 |
46 | send()
47 | {
48 | data="$1"
49 | if ${USE_PTT}
50 | then
51 | rigctl -m ${MODEL} -r ${DEVICE} set_ptt 1
52 | fi
53 | echo -n "${data}" | \
54 | ofdm-transfer -t -a \
55 | -r io \
56 | -s ${SAMPLE_RATE} \
57 | -f ${FREQUENCY} \
58 | -m ${MODULATION} \
59 | -b ${BIT_RATE} \
60 | -g ${AUDIO_GAIN} | \
61 | sox -q -t s16 -r ${SAMPLE_RATE} -c 1 -L - -t pulseaudio ${PCM_OUT}
62 | if ${USE_PTT}
63 | then
64 | rigctl -m ${MODEL} -r ${DEVICE} set_ptt 0
65 | fi
66 | }
67 |
68 | receive()
69 | {
70 | sox -q -t pulseaudio ${PCM_IN} -t s16 -r ${SAMPLE_RATE} -c 1 -L - | \
71 | ofdm-transfer -a -r io \
72 | -s ${SAMPLE_RATE} \
73 | -f ${FREQUENCY} \
74 | -m ${MODULATION} \
75 | -b ${BIT_RATE} \
76 | -g ${AUDIO_GAIN} &
77 | sox_pid=$(pidof "sox")
78 | ofdm_transfer_pid=$(pidof "ofdm-transfer")
79 | }
80 |
81 | main_loop()
82 | {
83 | echo "Ctrl-D to quit" 1>&2
84 | echo 1>&2
85 | receive
86 | IFS=""
87 | while true
88 | do
89 | stop=false
90 | read -r -d "" -t 1 data
91 | if [ $? -eq 1 -a -z "${data}" ]
92 | then
93 | break
94 | fi
95 | if [ ${#data} -eq 1 ]
96 | then
97 | ret=$(printf "%d" "'${data}")
98 | if [ ${ret} -eq 4 ]
99 | then
100 | break
101 | else
102 | send "${data}"
103 | fi
104 | elif [ -n "${data}" ]
105 | then
106 | send "${data}"
107 | fi
108 | done
109 | kill ${sox_pid} ${ofdm_transfer_pid}
110 | }
111 |
112 | main_loop
113 |
--------------------------------------------------------------------------------
/po/Makevars:
--------------------------------------------------------------------------------
1 | # Makefile variables for PO directory in any package using GNU gettext.
2 | #
3 | # Copyright (C) 2003-2019 Free Software Foundation, Inc.
4 | # This file is free software; the Free Software Foundation gives
5 | # unlimited permission to use, copy, distribute, and modify it.
6 |
7 | # Usually the message domain is the same as the package name.
8 | DOMAIN = $(PACKAGE)
9 |
10 | # These two variables depend on the location of this directory.
11 | subdir = po
12 | top_builddir = ..
13 |
14 | # These options get passed to xgettext.
15 | XGETTEXT_OPTIONS = --keyword=_ --keyword=N_
16 |
17 | # This is the copyright holder that gets inserted into the header of the
18 | # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding
19 | # package. (Note that the msgstr strings, extracted from the package's
20 | # sources, belong to the copyright holder of the package.) Translators are
21 | # expected to transfer the copyright for their translations to this person
22 | # or entity, or to disclaim their copyright. The empty string stands for
23 | # the public domain; in this case the translators are expected to disclaim
24 | # their copyright.
25 | COPYRIGHT_HOLDER = Guillaume LE VAILLANT
26 |
27 | # This tells whether or not to prepend "GNU " prefix to the package
28 | # name that gets inserted into the header of the $(DOMAIN).pot file.
29 | # Possible values are "yes", "no", or empty. If it is empty, try to
30 | # detect it automatically by scanning the files in $(top_srcdir) for
31 | # "GNU packagename" string.
32 | PACKAGE_GNU = no
33 |
34 | # This is the email address or URL to which the translators shall report
35 | # bugs in the untranslated strings:
36 | # - Strings which are not entire sentences, see the maintainer guidelines
37 | # in the GNU gettext documentation, section 'Preparing Strings'.
38 | # - Strings which use unclear terms or require additional context to be
39 | # understood.
40 | # - Strings which make invalid assumptions about notation of date, time or
41 | # money.
42 | # - Pluralisation problems.
43 | # - Incorrect English spelling.
44 | # - Incorrect formatting.
45 | # It can be your email address, or a mailing list address where translators
46 | # can write to without being subscribed, or the URL of a web page through
47 | # which the translators can contact you.
48 | MSGID_BUGS_ADDRESS =
49 |
50 | # This is the list of locale categories, beyond LC_MESSAGES, for which the
51 | # message catalogs shall be used. It is usually empty.
52 | EXTRA_LOCALE_CATEGORIES =
53 |
54 | # This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt'
55 | # context. Possible values are "yes" and "no". Set this to yes if the
56 | # package uses functions taking also a message context, like pgettext(), or
57 | # if in $(XGETTEXT_OPTIONS) you define keywords with a context argument.
58 | USE_MSGCTXT = no
59 |
60 | # These options get passed to msgmerge.
61 | # Useful options are in particular:
62 | # --previous to keep previous msgids of translated messages,
63 | # --quiet to reduce the verbosity.
64 | MSGMERGE_OPTIONS =
65 |
66 | # These options get passed to msginit.
67 | # If you want to disable line wrapping when writing PO files, add
68 | # --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and
69 | # MSGINIT_OPTIONS.
70 | MSGINIT_OPTIONS =
71 |
72 | # This tells whether or not to regenerate a PO file when $(DOMAIN).pot
73 | # has changed. Possible values are "yes" and "no". Set this to no if
74 | # the POT file is checked in the repository and the version control
75 | # program ignores timestamps.
76 | PO_DEPENDS_ON_POT = yes
77 |
78 | # This tells whether or not to forcibly update $(DOMAIN).pot and
79 | # regenerate PO files on "make dist". Possible values are "yes" and
80 | # "no". Set this to no if the POT file and PO files are maintained
81 | # externally.
82 | DIST_DEPENDS_ON_UPDATE_PO = yes
83 |
--------------------------------------------------------------------------------
/tests/test-program.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # This file is part of ofdm-transfer, a program to send or receive data
4 | # by software defined radio using the OFDM modulation.
5 | #
6 | # Copyright 2021-2022 Guillaume LE VAILLANT
7 | #
8 | # This program is free software: you can redistribute it and/or modify
9 | # it under the terms of the GNU General Public License as published by
10 | # the Free Software Foundation, either version 3 of the License, or
11 | # (at your option) any later version.
12 | #
13 | # This program is distributed in the hope that it will be useful,
14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | # GNU General Public License for more details.
17 | #
18 | # You should have received a copy of the GNU General Public License
19 | # along with this program. If not, see .
20 |
21 | set -e
22 |
23 | OFDM_TRANSFER=../src/ofdm-transfer
24 | MESSAGE=$(mktemp -t message.XXXXXX)
25 | DECODED=$(mktemp -t decoded.XXXXXX)
26 | SAMPLES=$(mktemp -t samples.XXXXXX)
27 |
28 | echo "This is a test transmission using ofdm-transfer." > ${MESSAGE}
29 |
30 | check_ok_io()
31 | {
32 | NAME=$1
33 | OPTIONS1=$2
34 | OPTIONS2=$3
35 |
36 | echo "Test: ${NAME}"
37 | ${OFDM_TRANSFER} -t -r io ${OPTIONS1} ${MESSAGE} > ${SAMPLES}
38 | ${OFDM_TRANSFER} -r io ${OPTIONS2} ${DECODED} < ${SAMPLES}
39 | diff -q ${MESSAGE} ${DECODED} > /dev/null
40 | }
41 |
42 | check_ok_file()
43 | {
44 | NAME=$1
45 | OPTIONS1=$2
46 | OPTIONS2=$3
47 |
48 | echo "Test: ${NAME}"
49 | ${OFDM_TRANSFER} -t -r file=${SAMPLES} ${OPTIONS1} ${MESSAGE}
50 | ${OFDM_TRANSFER} -r file=${SAMPLES} ${OPTIONS2} ${DECODED}
51 | diff -q ${MESSAGE} ${DECODED} > /dev/null
52 | }
53 |
54 | check_nok_io()
55 | {
56 | NAME=$1
57 | OPTIONS1=$2
58 | OPTIONS2=$3
59 |
60 | echo "Test: ${NAME}"
61 | ${OFDM_TRANSFER} -t -r io ${OPTIONS1} ${MESSAGE} > ${SAMPLES}
62 | ${OFDM_TRANSFER} -r io ${OPTIONS2} ${DECODED} < ${SAMPLES}
63 | ! diff -q ${MESSAGE} ${DECODED} > /dev/null
64 | }
65 |
66 | check_nok_file()
67 | {
68 | NAME=$1
69 | OPTIONS1=$2
70 | OPTIONS2=$3
71 |
72 | echo "Test: ${NAME}"
73 | ${OFDM_TRANSFER} -t -r file=${SAMPLES} ${OPTIONS1} ${MESSAGE}
74 | ${OFDM_TRANSFER} -r file=${SAMPLES} ${OPTIONS2} ${DECODED}
75 | ! diff -q ${MESSAGE} ${DECODED} > /dev/null
76 | }
77 |
78 | check_ok_io "Default parameters" "" ""
79 | check_ok_io "Bit rate 1200" "-b 1200" "-b 1200"
80 | check_ok_file "Bit rate 9600" "-b 9600" "-b 9600"
81 | check_ok_io "Bit rate 400000" "-b 400000" "-b 400000"
82 | check_nok_io "Wrong bit rate 9600 19200" "-b 9600" "-b 19200"
83 | check_ok_io "Frequency offset 200000" "-o 200000" "-o 200000"
84 | check_ok_file "Frequency offset -123456" "-o -123456" "-o -123456"
85 | check_nok_io "Wrong frequency offset 200000 250000" "-o 200000" "-o 250000"
86 | check_ok_io "Sample rate 4000000" "-s 4000000" "-s 4000000"
87 | check_ok_file "Sample rate 10000000" "-s 10000000" "-s 10000000"
88 | check_nok_io "Wrong sample rate 1000000 2000000" "-s 1000000" "-s 2000000"
89 | check_ok_io "Modulation apsk16" "-m apsk16" "-m apsk16"
90 | check_nok_file "Wrong modulation apsk16 qpsk" "-m apsk16" "-m qpsk"
91 | check_ok_file "Subcarrier number 100" "-n 100" "-n 100"
92 | check_nok_io "Wrong subcarrier number 64 128" "-n 64" "-n 128"
93 | check_ok_io "FEC Hamming(7/4)" "-e h74" "-e h74"
94 | check_ok_file "FEC Golay(24/12) and repeat(3)" "-e g2412,rep3" "-e g2412,rep3"
95 | check_ok_io "Id a1B2" "-i a1B2" "-i a1B2"
96 | check_nok_file "Wrong id ABCD ABC" "-i ABCD" "-i ABC"
97 | check_ok_file "Audio frequency 1500" \
98 | "-a -s 48000 -f 1500 -b 1200" \
99 | "-a -s 48000 -f 1500 -b 1200"
100 | check_nok_io "Wrong audio frequency 1500 2500" \
101 | "-a -s 48000 -f 1500 -b 1200" \
102 | "-a -s 48000 -f 2500 -b 1200"
103 | check_ok_io "Audio gain -20" \
104 | "-a -s 48000 -f 1500 -m bpsk -b 1200 -g -20" \
105 | "-a -s 48000 -f 1500 -m bpsk -b 1200"
106 |
107 | dd if=/dev/random of=${MESSAGE} bs=1000 count=1000 status=none
108 | check_ok_file "Bit rate 8000000 and sample rate 20000000" \
109 | "-s 20000000 -b 8000000" \
110 | "-s 20000000 -b 8000000"
111 |
112 | rm -f ${MESSAGE} ${DECODED} ${SAMPLES}
113 | echo "All tests passed."
114 |
--------------------------------------------------------------------------------
/tests/test-library-file.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of ofdm-transfer, a program to send or receive data
3 | by software defined radio using the OFDM modulation.
4 |
5 | Copyright 2021-2022 Guillaume LE VAILLANT
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include "ofdm-transfer.h"
26 |
27 | int identical(char *message_file, char *decoded_file)
28 | {
29 | FILE *message;
30 | FILE *decoded;
31 | unsigned int i1;
32 | unsigned int i2;
33 | unsigned char buffer1[1024];
34 | unsigned char buffer2[1024];
35 |
36 | if((message = fopen(message_file, "rb")) == NULL)
37 | {
38 | fprintf(stderr, "Error: Failed to open '%s'\n", message_file);
39 | return(0);
40 | }
41 | if((decoded = fopen(decoded_file, "rb")) == NULL)
42 | {
43 | fprintf(stderr, "Error: Failed to open '%s'\n", decoded_file);
44 | fclose(message);
45 | return(0);
46 | }
47 |
48 | while(1)
49 | {
50 | i1 = fread(buffer1, 1, 1024, message);
51 | i2 = fread(buffer2, 1, 1024, decoded);
52 | if((i1 == 0) && (i2 == 0))
53 | {
54 | break;
55 | }
56 | if((i1 != i2) || (memcmp(buffer1, buffer2, i1) != 0))
57 | {
58 | fclose(message);
59 | fclose(decoded);
60 | return(0);
61 | }
62 | }
63 |
64 | return(1);
65 | }
66 |
67 | int main()
68 | {
69 | ofdm_transfer_t send;
70 | ofdm_transfer_t receive;
71 | char message[] = "This is a test transmission using ofdm-transfer.";
72 | char message_file[] = "/tmp/message.XXXXXX";
73 | int message_fd = mkstemp(message_file);
74 | char decoded_file[] = "/tmp/decoded.XXXXXX";
75 | int decoded_fd = mkstemp(decoded_file);
76 | char samples_file[] = "/tmp/samples.XXXXXX";
77 | int samples_fd = mkstemp(samples_file);
78 | int ok = 0;
79 |
80 | fprintf(stderr, "Test: Send and receive file\n");
81 |
82 | if((message_fd == -1) || (decoded_fd == -1) || (samples_fd == -1))
83 | {
84 | fprintf(stderr, "Error: Failed to create temporary files\n");
85 | return(EXIT_FAILURE);
86 | }
87 | write(message_fd, message, strlen(message));
88 | close(message_fd);
89 | close(decoded_fd);
90 |
91 | if(dup2(samples_fd, STDIN_FILENO) == -1)
92 | {
93 | fprintf(stderr, "Error: Failed to redirect standard input\n");
94 | return(EXIT_FAILURE);
95 | }
96 | if(dup2(samples_fd, STDOUT_FILENO) == -1)
97 | {
98 | fprintf(stderr, "Error: Failed to redirect standard output\n");
99 | return(EXIT_FAILURE);
100 | }
101 |
102 | send = ofdm_transfer_create("io",
103 | 1,
104 | message_file,
105 | 2000000,
106 | 38400,
107 | 434000000,
108 | 0,
109 | "0",
110 | 0,
111 | "qpsk",
112 | 64,
113 | 16,
114 | 4,
115 | "h128",
116 | "none",
117 | "",
118 | NULL,
119 | 0,
120 | 0);
121 | if(send == NULL)
122 | {
123 | fprintf(stderr, "Error: Failed to initialize transfer\n");
124 | return(EXIT_FAILURE);
125 | }
126 | ofdm_transfer_start(send);
127 | ofdm_transfer_free(send);
128 |
129 | lseek(samples_fd, 0, SEEK_SET);
130 | receive = ofdm_transfer_create("io",
131 | 0,
132 | decoded_file,
133 | 2000000,
134 | 38400,
135 | 434000000,
136 | 0,
137 | "0",
138 | 0,
139 | "qpsk",
140 | 64,
141 | 16,
142 | 4,
143 | "h128",
144 | "none",
145 | "",
146 | NULL,
147 | 0,
148 | 0);
149 | if(receive == NULL)
150 | {
151 | fprintf(stderr, "Error: Failed to initialize transfer\n");
152 | return(EXIT_FAILURE);
153 | }
154 | ofdm_transfer_set_verbose(1);
155 | ofdm_transfer_start(receive);
156 | ofdm_transfer_free(receive);
157 |
158 | ok = identical(message_file, decoded_file);
159 | unlink(message_file);
160 | unlink(decoded_file);
161 | close(samples_fd);
162 | unlink(samples_file);
163 |
164 | if(ok)
165 | {
166 | return(EXIT_SUCCESS);
167 | }
168 | else
169 | {
170 | return(EXIT_FAILURE);
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/examples/full-duplex.c:
--------------------------------------------------------------------------------
1 | /*
2 | Example of use of ofdm-transfer's API to make a full-duplex link.
3 |
4 | Copyright 2022 Guillaume LE VAILLANT
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | #define DOWNLINK_RADIO "driver=rtlsdr"
28 | #define DOWNLINK_SAMPLE_RATE 250000
29 | #define DOWNLINK_GAIN "30"
30 | #define DOWNLINK_FREQUENCY_OFFSET 100000
31 | #define UPLINK_RADIO "driver=hackrf"
32 | #define UPLINK_SAMPLE_RATE 4000000
33 | #define UPLINK_GAIN "36"
34 | #define UPLINK_FREQUENCY_OFFSET 100000
35 | #define BIT_RATE 38400
36 | #define SUBCARRIER_MODULATION "bpsk"
37 | #define SUBCARRIERS 64
38 | #define CYCLIC_PREFIX_LENGTH 16
39 | #define TAPER_LENGTH 4
40 | #define INNER_FEC "none"
41 | #define OUTER_FEC "secded3932"
42 |
43 | void usage()
44 | {
45 | fprintf(stderr, "Usage:\n");
46 | fprintf(stderr, " full-duplex \n");
47 | }
48 |
49 | void signal_handler(int signum)
50 | {
51 | ofdm_transfer_stop_all();
52 | }
53 |
54 | void * transfer_thread(void *arg)
55 | {
56 | ofdm_transfer_t *transfer = (ofdm_transfer_t *) arg;
57 |
58 | ofdm_transfer_start(*transfer);
59 |
60 | return(NULL);
61 | }
62 |
63 | int main(int argc, char **argv)
64 | {
65 | unsigned long int downlink_frequency;
66 | ofdm_transfer_t downlink;
67 | pthread_t downlink_thread;
68 | unsigned long int uplink_frequency;
69 | ofdm_transfer_t uplink;
70 | pthread_t uplink_thread;
71 |
72 | if(argc != 3)
73 | {
74 | usage();
75 | return(EXIT_FAILURE);
76 | }
77 |
78 | downlink_frequency = strtoul(argv[1], NULL, 10);
79 | uplink_frequency = strtoul(argv[2], NULL, 10);
80 |
81 | downlink = ofdm_transfer_create(DOWNLINK_RADIO,
82 | 0,
83 | NULL,
84 | DOWNLINK_SAMPLE_RATE,
85 | BIT_RATE,
86 | downlink_frequency,
87 | DOWNLINK_FREQUENCY_OFFSET,
88 | DOWNLINK_GAIN,
89 | 0,
90 | SUBCARRIER_MODULATION,
91 | SUBCARRIERS,
92 | CYCLIC_PREFIX_LENGTH,
93 | TAPER_LENGTH,
94 | INNER_FEC,
95 | OUTER_FEC,
96 | "",
97 | NULL,
98 | 0,
99 | 0);
100 | if(downlink == NULL)
101 | {
102 | fprintf(stderr, "Error: Failed to initialize downlink.\n");
103 | return(EXIT_FAILURE);
104 | }
105 |
106 | uplink = ofdm_transfer_create(UPLINK_RADIO,
107 | 1,
108 | NULL,
109 | UPLINK_SAMPLE_RATE,
110 | BIT_RATE,
111 | uplink_frequency,
112 | UPLINK_FREQUENCY_OFFSET,
113 | UPLINK_GAIN,
114 | 0,
115 | SUBCARRIER_MODULATION,
116 | SUBCARRIERS,
117 | CYCLIC_PREFIX_LENGTH,
118 | TAPER_LENGTH,
119 | INNER_FEC,
120 | OUTER_FEC,
121 | "",
122 | NULL,
123 | 0,
124 | 0);
125 | if(uplink == NULL)
126 | {
127 | fprintf(stderr, "Error: Failed to initialize uplink.\n");
128 | return(EXIT_FAILURE);
129 | }
130 |
131 | if(pthread_create(&downlink_thread, NULL, transfer_thread, &downlink) != 0)
132 | {
133 | fprintf(stderr, "Error: Failed to start downlink thread.\n");
134 | return(EXIT_FAILURE);
135 | }
136 |
137 | /* Some function in the fftw library can cause errors when it is called from
138 | * several threads at the same time. Waiting 1 second before starting the
139 | * second thread seems to avoid the issue. */
140 | sleep(1);
141 |
142 | if(pthread_create(&uplink_thread, NULL, transfer_thread, &uplink) != 0)
143 | {
144 | fprintf(stderr, "Error: Failed to start uplink thread.\n");
145 | ofdm_transfer_stop_all();
146 | pthread_join(downlink_thread, NULL);
147 | ofdm_transfer_free(downlink);
148 | return(EXIT_FAILURE);
149 | }
150 |
151 | signal(SIGINT, &signal_handler);
152 | signal(SIGTERM, &signal_handler);
153 | signal(SIGABRT, &signal_handler);
154 | fprintf(stderr, "Use CTRL-C to quit.\n");
155 |
156 | pthread_join(uplink_thread, NULL);
157 | pthread_join(downlink_thread, NULL);
158 | ofdm_transfer_free(uplink);
159 | ofdm_transfer_free(downlink);
160 | fprintf(stderr, "\n");
161 |
162 | return(EXIT_SUCCESS);
163 | }
164 |
--------------------------------------------------------------------------------
/tests/test-library-callback.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of ofdm-transfer, a program to send or receive data
3 | by software defined radio using the OFDM modulation.
4 |
5 | Copyright 2021-2022 Guillaume LE VAILLANT
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include "ofdm-transfer.h"
26 |
27 | struct context_s
28 | {
29 | unsigned char data[128];
30 | unsigned int size;
31 | unsigned int index;
32 | };
33 |
34 | int read_data(void *context, unsigned char *payload, unsigned int payload_size)
35 | {
36 | struct context_s *ctx = (struct context_s *) context;
37 | unsigned int size = payload_size;
38 |
39 | if(ctx->index == ctx->size)
40 | {
41 | return(-1);
42 | }
43 | if(ctx->index + size > ctx->size)
44 | {
45 | size = ctx->size - ctx->index;
46 | }
47 | memcpy(payload, ctx->data + ctx->index, size);
48 | ctx->index += size;
49 |
50 | return(size);
51 | }
52 |
53 | int write_data(void *context, unsigned char *payload, unsigned int payload_size)
54 | {
55 | struct context_s *ctx = (struct context_s *) context;
56 |
57 | /* Note: The callback of a real application would make sure that it can write
58 | * all the payload without buffer overflow.
59 | */
60 | memcpy(ctx->data + ctx->size, payload, payload_size);
61 | ctx->size += payload_size;
62 |
63 | return(payload_size);
64 | }
65 |
66 | int main()
67 | {
68 | ofdm_transfer_t send;
69 | ofdm_transfer_t receive;
70 | struct context_s context;
71 | char message[] = "This is a test transmission using ofdm-transfer.";
72 | char samples_file[] = "/tmp/samples.XXXXXX";
73 | int samples_fd = mkstemp(samples_file);
74 | int ok = 0;
75 |
76 | fprintf(stderr, "Test: Send and receive using callbacks\n");
77 |
78 | strcpy(context.data, message);
79 | context.size = strlen(message);
80 | context.index = 0;
81 |
82 | if(samples_fd == -1)
83 | {
84 | fprintf(stderr, "Error: Failed to create temporary file\n");
85 | return(EXIT_FAILURE);
86 | }
87 |
88 | if(dup2(samples_fd, STDIN_FILENO) == -1)
89 | {
90 | fprintf(stderr, "Error: Failed to redirect standard input\n");
91 | return(EXIT_FAILURE);
92 | }
93 | if(dup2(samples_fd, STDOUT_FILENO) == -1)
94 | {
95 | fprintf(stderr, "Error: Failed to redirect standard output\n");
96 | return(EXIT_FAILURE);
97 | }
98 |
99 | send = ofdm_transfer_create_callback("io",
100 | 1,
101 | read_data,
102 | &context,
103 | 2000000,
104 | 9600,
105 | 434000000,
106 | 0,
107 | "0",
108 | 0,
109 | "qpsk",
110 | 64,
111 | 16,
112 | 4,
113 | "h128",
114 | "none",
115 | "",
116 | NULL,
117 | 0,
118 | 0);
119 | if(send == NULL)
120 | {
121 | fprintf(stderr, "Error: Failed to initialize transfer\n");
122 | return(EXIT_FAILURE);
123 | }
124 | ofdm_transfer_start(send);
125 | ofdm_transfer_free(send);
126 |
127 | lseek(samples_fd, 0, SEEK_SET);
128 | bzero(&context, sizeof(context));
129 | receive = ofdm_transfer_create_callback("io",
130 | 0,
131 | write_data,
132 | &context,
133 | 2000000,
134 | 9600,
135 | 434000000,
136 | 0,
137 | "0",
138 | 0,
139 | "qpsk",
140 | 64,
141 | 16,
142 | 4,
143 | "h128",
144 | "none",
145 | "",
146 | NULL,
147 | 0,
148 | 0);
149 | if(receive == NULL)
150 | {
151 | fprintf(stderr, "Error: Failed to initialize transfer\n");
152 | return(EXIT_FAILURE);
153 | }
154 | ofdm_transfer_start(receive);
155 | ofdm_transfer_free(receive);
156 |
157 | ok = (strcmp(message, context.data) == 0);
158 | close(samples_fd);
159 | unlink(samples_file);
160 |
161 | if(ok)
162 | {
163 | return(EXIT_SUCCESS);
164 | }
165 | else
166 | {
167 | return(EXIT_FAILURE);
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/ofdm-transfer.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of ofdm-transfer, a program to send or receive data
3 | by software defined radio using the OFDM modulation.
4 |
5 | Copyright 2021-2022 Guillaume LE VAILLANT
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 | */
20 |
21 | #ifndef OFDM_TRANSFER_H
22 | #define OFDM_TRANSFER_H
23 |
24 | typedef struct ofdm_transfer_s *ofdm_transfer_t;
25 |
26 | /* Set the verbosity level
27 | * - v: if not 0, print some debug messages to stderr
28 | */
29 | void ofdm_transfer_set_verbose(unsigned char v);
30 |
31 | /* Get the verbosity level */
32 | unsigned char ofdm_transfer_is_verbose();
33 |
34 | /* Initialize a new transfer
35 | * - radio_driver: radio to use (e.g. "io" or "driver=hackrf")
36 | * - emit: 1 for transmit mode; 0 for receive mode
37 | * - file: in transmit mode, read data from this file
38 | * in receive mode, write data to this file
39 | * if NULL, use stdin or stdout instead
40 | * - sample_rate: samples per second
41 | * - bit_rate: bits per second
42 | * - frequency: center frequency of the transfer in Hertz
43 | * - frequency_offset: set the frequency of the radio frequency_offset Hz
44 | * lower than the frequency of the transfer (can be negative to set the
45 | * frequency higher)
46 | * - gain: gain of the radio transceiver
47 | * - ppm: correction for the radio clock
48 | * - subcarrier_modulation: modulation to use for the subcarriers
49 | * - subcarriers: number of subcarriers
50 | * - cyclic_prefix_length: length of the cyclic prefix
51 | * - taper_length: length of the taper
52 | * - inner_fec: inner forward error correction code to use
53 | * - outer_fec: outer forward error correction code to use
54 | * - id: transfer id; when receiving, frames with a different id will be
55 | * ignored
56 | * - dump: if not NULL, write raw samples sent or received to this file
57 | * - timeout: number of seconds after which reception will be stopped if no
58 | * frame has been received; 0 means no timeout
59 | * - audio: 0 to use IQ samples, 1 to use audio samples
60 | *
61 | * If the transfer initialization fails, the function returns NULL.
62 | */
63 | ofdm_transfer_t ofdm_transfer_create(char *radio_driver,
64 | unsigned char emit,
65 | char *file,
66 | unsigned long int sample_rate,
67 | unsigned int bit_rate,
68 | unsigned long int frequency,
69 | long int frequency_offset,
70 | char *gain,
71 | float ppm,
72 | char *subcarrier_modulation,
73 | unsigned int subcarriers,
74 | unsigned int cyclic_prefix_length,
75 | unsigned int taper_length,
76 | char *inner_fec,
77 | char *outer_fec,
78 | char *id,
79 | char *dump,
80 | unsigned int timeout,
81 | unsigned char audio);
82 |
83 | /* Initialize a new transfer using a callback
84 | * The parameters are the same as ofdm_transfer_create() except that the 'file'
85 | * string is replaced by the 'data_callback' function pointer and the
86 | * 'callback_context' pointer.
87 | * The callback function must have the following type:
88 | *
89 | * int callback(void *context,
90 | * unsigned char *payload,
91 | * unsigned int payload_size)
92 | *
93 | * When emitting, the callback must try to read 'payload_size' bytes from
94 | * somewhere and put them into 'payload'. It must return the number of bytes
95 | * read, or -1 if the input stream is finished.
96 | * When receiving, the callback must take 'payload_size' bytes from 'payload'
97 | * and write them somewhere. It must return only when all the bytes have been
98 | * written. The returned value should be the number of bytes written, but
99 | * currently it is not used.
100 | * The user-specified 'callback_context' pointer is passed to the callback
101 | * as 'context'.
102 | */
103 | ofdm_transfer_t ofdm_transfer_create_callback(char *radio_driver,
104 | unsigned char emit,
105 | int (*data_callback)(void *,
106 | unsigned char *,
107 | unsigned int),
108 | void *callback_context,
109 | unsigned long int sample_rate,
110 | unsigned int bit_rate,
111 | unsigned long int frequency,
112 | long int frequency_offset,
113 | char *gain,
114 | float ppm,
115 | char *subcarrier_modulation,
116 | unsigned int subcarriers,
117 | unsigned int cyclic_prefix_length,
118 | unsigned int taper_length,
119 | char *inner_fec,
120 | char *outer_fec,
121 | char *id,
122 | char *dump,
123 | unsigned int timeout,
124 | unsigned char audio);
125 |
126 | /* Cleanup after a finished transfer */
127 | void ofdm_transfer_free(ofdm_transfer_t transfer);
128 |
129 | /* Start a transfer and return when finished */
130 | void ofdm_transfer_start(ofdm_transfer_t transfer);
131 |
132 | /* Interrupt a transfer */
133 | void ofdm_transfer_stop(ofdm_transfer_t transfer);
134 |
135 | /* Interrupt all transfers */
136 | void ofdm_transfer_stop_all();
137 |
138 | /* Print list of detected software defined radios */
139 | void ofdm_transfer_print_available_radios();
140 |
141 | /* Print list of supported subcarrier modulations */
142 | void ofdm_transfer_print_available_subcarrier_modulations();
143 |
144 | /* Print list of supported forward error codes */
145 | void ofdm_transfer_print_available_forward_error_codes();
146 |
147 | #endif
148 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | ofdm-transfer
2 | =============
3 |
4 | The active repository is at https://codeberg.org/glv/ofdm-transfer
5 |
6 | ofdm-transfer is a command-line program to send or receive data by software
7 | defined radio using the OFDM modulation.
8 |
9 |
10 | ## Usage
11 |
12 | ofdm-transfer [options] [filename]
13 |
14 | Options:
15 | -a
16 | Use audio samples instead of IQ samples.
17 | -b (default: 38400 b/s)
18 | Bit rate of the OFDM transmission.
19 | -c (default: 0.0, can be negative)
20 | Correction for the radio clock.
21 | -d
22 | Dump a copy of the samples sent to or received from
23 | the radio.
24 | -e (default: h128,none)
25 | Inner and outer forward error correction codes to use.
26 | -f (default: 434000000 Hz)
27 | Frequency of the OFDM transmission.
28 | -g (default: 0)
29 | Gain of the radio transceiver, or audio gain in dB.
30 | -h
31 | This help.
32 | -i (default: "")
33 | Transfer id (at most 4 bytes). When receiving, the frames
34 | with a different id will be ignored.
35 | -m (default: qpsk)
36 | Modulation to use for the subcarriers.
37 | -n (default: 64,16,4)
38 | Number of subcarriers, cyclic prefix length and taper length
39 | of the OFDM transmission.
40 | -o (default: 0 Hz, can be negative)
41 | Set the central frequency of the transceiver 'offset' Hz
42 | lower than the signal frequency to send or receive.
43 | -r (default: "")
44 | Radio to use.
45 | -s (default: 2000000 S/s)
46 | Sample rate to use.
47 | -T (default: 0 s)
48 | Number of seconds after which reception will be stopped if
49 | no frame has been received. A timeout of 0 means no timeout.
50 | -t
51 | Use transmit mode.
52 | -v
53 | Print debug messages.
54 | -w (default: 0.0 s)
55 | Wait a little before switching the radio off.
56 | This can be useful if the hardware needs some time to send
57 | the last samples it has buffered.
58 |
59 | By default the program is in 'receive' mode.
60 | Use the '-t' option to use the 'transmit' mode.
61 |
62 | In 'receive' mode, the samples are received from the radio,
63 | and the decoded data is written either to 'filename' if it
64 | is specified, or to standard output.
65 | In 'transmit' mode, the data to send is read either from
66 | 'filename' if it is specified, or from standard input,
67 | and the samples are sent to the radio.
68 |
69 | Instead of a real radio transceiver, the 'io' radio type uses
70 | standard input in 'receive' mode, and standard output in
71 | 'transmit' mode.
72 | The 'file=path-to-file' radio type reads/writes the samples
73 | from/to 'path-to-file'.
74 | The IQ samples must be in 'complex float' format
75 | (32 bits for the real part, 32 bits for the imaginary part).
76 | The audio samples must be in 'signed integer' format (16 bits).
77 |
78 | The gain parameter can be specified either as an integer to set a
79 | global gain, or as a series of keys and values to set specific
80 | gains (for example 'LNA=32,VGA=20').
81 | When using the audio mode (with the '-a' option), the gain value
82 | in dB is applied to the audio samples.
83 |
84 |
85 | ## Compilation
86 |
87 | ofdm-transfer requires the following dependencies:
88 | - libliquid (https://github.com/jgaeddert/liquid-dsp)
89 | - libSoapySDR (https://github.com/pothosware/SoapySDR)
90 |
91 | It can be compiled with the usual:
92 |
93 | ./autogen.sh
94 | ./configure
95 | make
96 |
97 |
98 | ## Supported radios
99 |
100 | ofdm-transfer uses the SoapySDR API, therefore if your radio is supported by
101 | SoapySDR and you have the right driver installed, it should work.
102 |
103 | When used with the '-h' option to print the help message, ofdm-transfer will
104 | also list the radios that it has detected.
105 |
106 |
107 | ## Supported subcarrier modulations
108 | - bpsk
109 | - qpsk
110 | - psk8
111 | - apsk16
112 | - apsk32
113 | - apsk64
114 | - apsk128
115 | - apsk256
116 |
117 |
118 | ## Supported FEC codes
119 |
120 | The forward error correction codes that can be specified with the '-e' option
121 | are the codes that are supported by libliquid.
122 |
123 | These should always be available:
124 | - none
125 | - rep3: repeat(3)
126 | - rep5: repeat(5)
127 | - h74: Hamming(7,4)
128 | - h84: Hamming(8,4)
129 | - h128: Hamming(12,8)
130 | - g2412: Golay(24,12)
131 | - secded2216: SEC-DED(22,16)
132 | - secded3932: SEC-DED(39,32)
133 | - secded7264: SEC-DED(72,64)
134 |
135 | If your libliquid library has been compiled with support for the libfec
136 | library, the following codes should also be available:
137 | - v27: convolutional r1/2 K=7
138 | - v29: convolutional r1/2 K=9
139 | - v39: convolutional r1/3 K=9
140 | - v615: convolutional r1/6 K=15
141 | - v27p23: convolutional r2/3 K=7 (punctured)
142 | - v27p34: convolutional r3/4 K=7 (punctured)
143 | - v27p45: convolutional r4/5 K=7 (punctured)
144 | - v27p56: convolutional r5/6 K=7 (punctured)
145 | - v27p67: convolutional r6/7 K=7 (punctured)
146 | - v27p78: convolutional r7/8 K=7 (punctured)
147 | - v29p23: convolutional r2/3 K=9 (punctured)
148 | - v29p34: convolutional r3/4 K=9 (punctured)
149 | - v29p45: convolutional r4/5 K=9 (punctured)
150 | - v29p56: convolutional r5/6 K=9 (punctured)
151 | - v29p67: convolutional r6/7 K=9 (punctured)
152 | - v29p78: convolutional r7/8 K=9 (punctured)
153 | - rs8: Reed-Solomon, 223/255
154 |
155 |
156 | ## Bandwidth
157 |
158 | The occupied bandwidth (99% power) of the generated signal is approximately
159 | (bit_rate * 1.6 * k) Hertz.
160 |
161 | Where k is a coefficient depending on the subcarrier modulation:
162 | - bpsk: 1
163 | - qpsk: 1/2
164 | - psk8: 1/3
165 | - apsk16: 1/4
166 | - apsk32: 1/5
167 | - apsk64: 1/6
168 | - apsk128: 1/7
169 | - apsk256: 1/8
170 |
171 |
172 | ## Examples
173 |
174 | Send a file at 9600 b/s on 434 MHz using a HackRF:
175 |
176 | ofdm-transfer -t \
177 | -r driver=hackrf \
178 | -s 4000000 \
179 | -f 434000000 \
180 | -o 100000 \
181 | -b 9600 \
182 | -g 30 \
183 | -w 1 \
184 | input_file
185 |
186 | Receive a file at 9600 b/s on 434 MHz using a RTL-SDR:
187 |
188 | ofdm-transfer -r driver=rtlsdr \
189 | -s 2000000 \
190 | -f 434000000 \
191 | -o 100000 \
192 | -b 9600 \
193 | -g 20 \
194 | -T 30 \
195 | output_file
196 |
197 | Generate audio samples for a 1200 b/s transmission centered at 1500 Hz and play
198 | them:
199 |
200 | ofdm-transfer -t \
201 | -r file=/tmp/samples.s16 \
202 | -a \
203 | -s 48000 \
204 | -f 1500 \
205 | -b 1200 \
206 | input_file
207 | aplay -f S16_LE -r 48000 -c 1 /tmp/samples.s16
208 |
209 |
210 | Send a file at 48 kb/s using an audio cable:
211 |
212 | cat file.dat | ofdm-transfer -t -a -r io -s 48000 -f 12000 -m apsk16 -b 48000 | aplay -q -f S16_LE -r 48000 -c 1
213 |
214 |
215 | Receive a file at 48 kb/s using an audio cable:
216 |
217 | arecord -q -f S16_LE -r 48000 -c 1 | ofdm-transfer -a -r io -s 48000 -f 12000 -m apsk16 -b 48000 -T 10 > file.dat
218 |
219 |
220 | ## Library
221 |
222 | You can add OFDM transfer support to your programs easily by using the
223 | 'libofdm-transfer' library.
224 | The API is described in the 'ofdm-transfer.h' file.
225 |
226 | The 'echo-server' example program shows how to use the API to make a server
227 | receiving messages from clients and sending them back in reverse order.
228 |
229 | The 'full-duplex' example program shows how to use the API to make
230 | a full-duplex link using two devices.
231 |
232 | The 'full-duplex-ppp.sh' script shows how to make a PPP connection between two
233 | machines using the 'full-duplex' example program.
234 |
235 | The 'half-duplex.sh' script shows how to make a half-duplex link between two
236 | (not SDR) transceivers using audio, like a sound modem TNC.
237 |
--------------------------------------------------------------------------------
/examples/echo-server.c:
--------------------------------------------------------------------------------
1 | /*
2 | Example of use of ofdm-transfer's API to make a server receiving
3 | messages from clients and sending them back in reverse order.
4 |
5 | Copyright 2021-2022 Guillaume LE VAILLANT
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | #define RADIO_DRIVER "driver=hackrf"
29 | #define SAMPLE_RATE 4000000
30 | #define TRANSMISSION_GAIN "36"
31 | #define RECEPTION_GAIN "60"
32 | #define FREQUENCY_OFFSET 100000
33 | #define BIT_RATE 9600
34 | #define SUBCARRIER_MODULATION "qpsk"
35 | #define SUBCARRIERS 64
36 | #define CYCLIC_PREFIX_LENGTH 16
37 | #define TAPER_LENGTH 4
38 | #define INNER_FEC "rs8"
39 | #define OUTER_FEC "rs8"
40 |
41 | struct message_s
42 | {
43 | unsigned char *data;
44 | unsigned int size;
45 | unsigned int done;
46 | };
47 |
48 | unsigned char stop_loop = 0;
49 |
50 | int transmission_callback(void *context,
51 | unsigned char *payload,
52 | unsigned int payload_size)
53 | {
54 | struct message_s *message = (struct message_s *) context;
55 | unsigned int size = message->size - message->done;
56 |
57 | if(size == 0)
58 | {
59 | return(-1);
60 | }
61 | size = (payload_size < size) ? payload_size : size;
62 | memcpy(payload, &message->data[message->done], size);
63 | message->done += size;
64 | return(size);
65 | }
66 |
67 | void transmit(unsigned char *data,
68 | unsigned int size,
69 | unsigned long int frequency)
70 | {
71 | struct message_s message = {data, size, 0};
72 | ofdm_transfer_t transfer = ofdm_transfer_create_callback(RADIO_DRIVER,
73 | 1,
74 | transmission_callback,
75 | (void *) &message,
76 | SAMPLE_RATE,
77 | BIT_RATE,
78 | frequency,
79 | FREQUENCY_OFFSET,
80 | TRANSMISSION_GAIN,
81 | 0,
82 | SUBCARRIER_MODULATION,
83 | SUBCARRIERS,
84 | CYCLIC_PREFIX_LENGTH,
85 | TAPER_LENGTH,
86 | INNER_FEC,
87 | OUTER_FEC,
88 | "",
89 | NULL,
90 | 0,
91 | 0);
92 | if(transfer == NULL)
93 | {
94 | return;
95 | }
96 | ofdm_transfer_start(transfer);
97 | sleep(1); /* Give time to the hackrf to send the last samples */
98 | ofdm_transfer_free(transfer);
99 | }
100 |
101 | int reception_callback(void *context,
102 | unsigned char *payload,
103 | unsigned int payload_size)
104 | {
105 | struct message_s *message = (struct message_s *) context;
106 | unsigned int size = message->size;
107 |
108 | size = (payload_size < size) ? payload_size : size;
109 | memcpy(message->data, payload, size);
110 | message->done = size;
111 | ofdm_transfer_stop_all();
112 | return(payload_size);
113 | }
114 |
115 | void receive_1(unsigned char *data,
116 | unsigned int *size,
117 | unsigned long int frequency)
118 | {
119 | struct message_s message = {data, *size, 0};
120 | ofdm_transfer_t transfer = ofdm_transfer_create_callback(RADIO_DRIVER,
121 | 0,
122 | reception_callback,
123 | (void *) &message,
124 | SAMPLE_RATE,
125 | BIT_RATE,
126 | frequency,
127 | FREQUENCY_OFFSET,
128 | RECEPTION_GAIN,
129 | 0,
130 | SUBCARRIER_MODULATION,
131 | SUBCARRIERS,
132 | CYCLIC_PREFIX_LENGTH,
133 | TAPER_LENGTH,
134 | INNER_FEC,
135 | OUTER_FEC,
136 | "",
137 | NULL,
138 | 0,
139 | 0);
140 | if(transfer == NULL)
141 | {
142 | return;
143 | }
144 | ofdm_transfer_start(transfer);
145 | ofdm_transfer_free(transfer);
146 | *size = message.done;
147 | }
148 |
149 | void process_request(unsigned char *data, unsigned int size)
150 | {
151 | unsigned int i;
152 | unsigned char c;
153 |
154 | for(i = 0; i < size / 2; i++)
155 | {
156 | c = data[i];
157 | data[i] = data[size - 1 - i];
158 | data[size - 1 - i] = c;
159 | }
160 | }
161 |
162 | void server(unsigned long int frequency)
163 | {
164 | unsigned char data[1024];
165 | unsigned int size;
166 |
167 | while(!stop_loop)
168 | {
169 | size = sizeof(data) - 1;
170 | receive_1(data, &size, frequency);
171 | if(stop_loop)
172 | {
173 | return;
174 | }
175 | data[size] = '\0';
176 | printf("\nReceived: %s\n", data);
177 | process_request(data, size);
178 | printf("Sending: %s\n", data);
179 | sleep(1);
180 | if(stop_loop)
181 | {
182 | return;
183 | }
184 | transmit(data, size, frequency);
185 | }
186 | }
187 |
188 | void client(unsigned char *data, unsigned int size, unsigned long int frequency)
189 | {
190 | unsigned char buffer[1024];
191 | unsigned int n = sizeof(buffer) - 1;
192 |
193 | printf("\nSending: %s\n", data);
194 | transmit(data, size, frequency);
195 | receive_1(buffer, &n, frequency);
196 | buffer[n] = '\0';
197 | printf("Received: %s\n", buffer);
198 | }
199 |
200 | void signal_handler(int signum)
201 | {
202 | fprintf(stderr, "\n");
203 | ofdm_transfer_stop_all();
204 | stop_loop = 1;
205 | }
206 |
207 | void usage()
208 | {
209 | fprintf(stderr, "Usage:\n");
210 | fprintf(stderr, " echo-server server \n");
211 | fprintf(stderr, " echo-server client \n");
212 | }
213 |
214 | int main(int argc, char **argv)
215 | {
216 | unsigned long int frequency;
217 | unsigned char *mode;
218 | unsigned char *data;
219 |
220 | if((argc != 3) && (argc != 4))
221 | {
222 | usage();
223 | return(-1);
224 | }
225 |
226 | mode = argv[1];
227 | frequency = strtoul(argv[2], NULL, 10);
228 | stop_loop = 0;
229 | signal(SIGINT, &signal_handler);
230 | signal(SIGTERM, &signal_handler);
231 | signal(SIGABRT, &signal_handler);
232 |
233 | if(strcmp(mode, "client") == 0)
234 | {
235 | if(argc !=4)
236 | {
237 | usage();
238 | return(-1);
239 | }
240 | data = argv[3];
241 | client(data, strlen(data), frequency);
242 | }
243 | else if(strcmp(mode, "server") == 0)
244 | {
245 | server(frequency);
246 | }
247 | else
248 | {
249 | usage();
250 | return(-1);
251 | }
252 | return(0);
253 | }
254 |
--------------------------------------------------------------------------------
/po/ofdm-transfer.pot:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR Guillaume LE VAILLANT
3 | # This file is distributed under the same license as the ofdm-transfer package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: ofdm-transfer 1.8.0\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2022-11-28 11:19+0100\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=CHARSET\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #: src/main.c:37
21 | #, c-format
22 | msgid ""
23 | "\n"
24 | "Stopping (signal %d)\n"
25 | msgstr ""
26 |
27 | #: src/main.c:48
28 | #, c-format
29 | msgid "ofdm-transfer version 1.8.0\n"
30 | msgstr ""
31 |
32 | #: src/main.c:50
33 | #, c-format
34 | msgid "Usage: ofdm-transfer [options] [filename]\n"
35 | msgstr ""
36 |
37 | #: src/main.c:52
38 | #, c-format
39 | msgid "Options:\n"
40 | msgstr ""
41 |
42 | #: src/main.c:54
43 | #, c-format
44 | msgid " Use audio samples instead of IQ samples.\n"
45 | msgstr ""
46 |
47 | #: src/main.c:55
48 | #, c-format
49 | msgid " -b (default: 38400 b/s)\n"
50 | msgstr ""
51 |
52 | #: src/main.c:56
53 | #, c-format
54 | msgid " Bit rate of the OFDM transmission.\n"
55 | msgstr ""
56 |
57 | #: src/main.c:57
58 | #, c-format
59 | msgid " -c (default: 0.0, can be negative)\n"
60 | msgstr ""
61 |
62 | #: src/main.c:58
63 | #, c-format
64 | msgid " Correction for the radio clock.\n"
65 | msgstr ""
66 |
67 | #: src/main.c:59
68 | #, c-format
69 | msgid " -d \n"
70 | msgstr ""
71 |
72 | #: src/main.c:60
73 | #, c-format
74 | msgid ""
75 | " Dump a copy of the samples sent to or received from\n"
76 | " the radio.\n"
77 | msgstr ""
78 |
79 | #: src/main.c:62
80 | #, c-format
81 | msgid " -e (default: h128,none)\n"
82 | msgstr ""
83 |
84 | #: src/main.c:63
85 | #, c-format
86 | msgid " Inner and outer forward error correction codes to use.\n"
87 | msgstr ""
88 |
89 | #: src/main.c:64
90 | #, c-format
91 | msgid " -f (default: 434000000 Hz)\n"
92 | msgstr ""
93 |
94 | #: src/main.c:65
95 | #, c-format
96 | msgid " Frequency of the OFDM transmission.\n"
97 | msgstr ""
98 |
99 | #: src/main.c:66
100 | #, c-format
101 | msgid " -g (default: 0)\n"
102 | msgstr ""
103 |
104 | #: src/main.c:67
105 | #, c-format
106 | msgid " Gain of the radio transceiver, or audio gain in dB.\n"
107 | msgstr ""
108 |
109 | #: src/main.c:69
110 | #, c-format
111 | msgid " This help.\n"
112 | msgstr ""
113 |
114 | #: src/main.c:70
115 | #, c-format
116 | msgid " -i (default: \"\")\n"
117 | msgstr ""
118 |
119 | #: src/main.c:71
120 | #, c-format
121 | msgid ""
122 | " Transfer id (at most 4 bytes). When receiving, the frames\n"
123 | " with a different id will be ignored.\n"
124 | msgstr ""
125 |
126 | #: src/main.c:73
127 | #, c-format
128 | msgid " -m (default: qpsk)\n"
129 | msgstr ""
130 |
131 | #: src/main.c:74
132 | #, c-format
133 | msgid " Modulation to use for the subcarriers.\n"
134 | msgstr ""
135 |
136 | #: src/main.c:75
137 | #, c-format
138 | msgid " -n (default: 64,16,4)\n"
139 | msgstr ""
140 |
141 | #: src/main.c:76
142 | #, c-format
143 | msgid ""
144 | " Number of subcarriers, cyclic prefix length and taper length\n"
145 | " of the OFDM transmission.\n"
146 | msgstr ""
147 |
148 | #: src/main.c:78
149 | #, c-format
150 | msgid " -o (default: 0 Hz, can be negative)\n"
151 | msgstr ""
152 |
153 | #: src/main.c:79
154 | #, c-format
155 | msgid ""
156 | " Set the central frequency of the transceiver 'offset' Hz\n"
157 | " lower than the signal frequency to send or receive.\n"
158 | msgstr ""
159 |
160 | #: src/main.c:81
161 | #, c-format
162 | msgid " -r (default: \"\")\n"
163 | msgstr ""
164 |
165 | #: src/main.c:82
166 | #, c-format
167 | msgid " Radio to use.\n"
168 | msgstr ""
169 |
170 | #: src/main.c:83
171 | #, c-format
172 | msgid " -s (default: 2000000 S/s)\n"
173 | msgstr ""
174 |
175 | #: src/main.c:84
176 | #, c-format
177 | msgid " Sample rate to use.\n"
178 | msgstr ""
179 |
180 | #: src/main.c:85
181 | #, c-format
182 | msgid " -T (default: 0 s)\n"
183 | msgstr ""
184 |
185 | #: src/main.c:86
186 | #, c-format
187 | msgid ""
188 | " Number of seconds after which reception will be stopped if\n"
189 | " no frame has been received. A timeout of 0 means no timeout.\n"
190 | msgstr ""
191 |
192 | #: src/main.c:89
193 | #, c-format
194 | msgid " Use transmit mode.\n"
195 | msgstr ""
196 |
197 | #: src/main.c:91
198 | #, c-format
199 | msgid " Print debug messages.\n"
200 | msgstr ""
201 |
202 | #: src/main.c:92
203 | #, c-format
204 | msgid " -w (default: 0.0 s)\n"
205 | msgstr ""
206 |
207 | #: src/main.c:93
208 | #, c-format
209 | msgid ""
210 | " Wait a little before switching the radio off.\n"
211 | " This can be useful if the hardware needs some time to send\n"
212 | " the last samples it has buffered.\n"
213 | msgstr ""
214 |
215 | #: src/main.c:97
216 | #, c-format
217 | msgid ""
218 | "By default the program is in 'receive' mode.\n"
219 | "Use the '-t' option to use the 'transmit' mode.\n"
220 | msgstr ""
221 |
222 | #: src/main.c:100
223 | #, c-format
224 | msgid ""
225 | "In 'receive' mode, the samples are received from the radio,\n"
226 | "and the decoded data is written either to 'filename' if it\n"
227 | "is specified, or to standard output.\n"
228 | "In 'transmit' mode, the data to send is read either from\n"
229 | "'filename' if it is specified, or from standard input,\n"
230 | "and the samples are sent to the radio.\n"
231 | msgstr ""
232 |
233 | #: src/main.c:107
234 | #, c-format
235 | msgid ""
236 | "Instead of a real radio transceiver, the 'io' radio type uses\n"
237 | "standard input in 'receive' mode, and standard output in\n"
238 | "'transmit' mode.\n"
239 | "The 'file=path-to-file' radio type reads/writes the samples\n"
240 | "from/to 'path-to-file'.\n"
241 | "The IQ samples must be in 'complex float' format\n"
242 | "(32 bits for the real part, 32 bits for the imaginary part).\n"
243 | "The audio samples must be in 'signed integer' format (16 bits).\n"
244 | msgstr ""
245 |
246 | #: src/main.c:116
247 | #, c-format
248 | msgid ""
249 | "The gain parameter can be specified either as an integer to set a\n"
250 | "global gain, or as a series of keys and values to set specific\n"
251 | "gains (for example 'LNA=32,VGA=20').\n"
252 | "When using the audio mode (with the '-a' option), the gain value\n"
253 | "in dB is applied to the audio samples.\n"
254 | msgstr ""
255 |
256 | #: src/main.c:122
257 | #, c-format
258 | msgid "Available radios (via SoapySDR):\n"
259 | msgstr ""
260 |
261 | #: src/main.c:125
262 | #, c-format
263 | msgid "Available subcarrier modulations:\n"
264 | msgstr ""
265 |
266 | #: src/main.c:128
267 | #, c-format
268 | msgid "Available forward error correction codes:\n"
269 | msgstr ""
270 |
271 | #: src/main.c:326
272 | #, c-format
273 | msgid "Error: Unknown parameter: '-%c %s'\n"
274 | msgstr ""
275 |
276 | #: src/main.c:364
277 | #, c-format
278 | msgid "Error: Failed to initialize transfer\n"
279 | msgstr ""
280 |
281 | #: src/ofdm-transfer.c:49 src/ofdm-transfer.c:947 src/ofdm-transfer.c:992
282 | #, c-format
283 | msgid "Error: %s\n"
284 | msgstr ""
285 |
286 | #: src/ofdm-transfer.c:482 src/ofdm-transfer.c:657 src/ofdm-transfer.c:757
287 | #, c-format
288 | msgid "Error: Memory allocation failed\n"
289 | msgstr ""
290 |
291 | #: src/ofdm-transfer.c:609
292 | #, c-format
293 | msgid "Frame %u for '%s': corrupted header\n"
294 | msgstr ""
295 |
296 | #: src/ofdm-transfer.c:613
297 | #, c-format
298 | msgid "Frame %u for '%s': corrupted payload\n"
299 | msgstr ""
300 |
301 | #: src/ofdm-transfer.c:622
302 | #, c-format
303 | msgid "Frame %u for '%s': ignored\n"
304 | msgstr ""
305 |
306 | #: src/ofdm-transfer.c:691
307 | #, c-format
308 | msgid "Timeout: %d s without frames\n"
309 | msgstr ""
310 |
311 | #: src/ofdm-transfer.c:787
312 | #, c-format
313 | msgid "Error: Invalid sample rate\n"
314 | msgstr ""
315 |
316 | #: src/ofdm-transfer.c:798
317 | #, c-format
318 | msgid "Error: Invalid frequency\n"
319 | msgstr ""
320 |
321 | #: src/ofdm-transfer.c:821
322 | #, c-format
323 | msgid "Error: This radio type only supports IQ samples\n"
324 | msgstr ""
325 |
326 | #: src/ofdm-transfer.c:838
327 | #, c-format
328 | msgid "Error: Invalid bit rate\n"
329 | msgstr ""
330 |
331 | #: src/ofdm-transfer.c:857
332 | #, c-format
333 | msgid "Error: Invalid subcarrier modulation\n"
334 | msgstr ""
335 |
336 | #: src/ofdm-transfer.c:868
337 | #, c-format
338 | msgid "Error: Invalid number of subcarriers\n"
339 | msgstr ""
340 |
341 | #: src/ofdm-transfer.c:880
342 | #, c-format
343 | msgid "Error: Invalid inner FEC\n"
344 | msgstr ""
345 |
346 | #: src/ofdm-transfer.c:888
347 | #, c-format
348 | msgid "Error: Invalid outer FEC\n"
349 | msgstr ""
350 |
351 | #: src/ofdm-transfer.c:899
352 | #, c-format
353 | msgid "Error: Id must be at most 4 bytes long\n"
354 | msgstr ""
355 |
356 | #: src/ofdm-transfer.c:909 src/ofdm-transfer.c:937 src/ofdm-transfer.c:1070
357 | #, c-format
358 | msgid "Error: Failed to open '%s'\n"
359 | msgstr ""
360 |
361 | #: src/ofdm-transfer.c:1000
362 | #, c-format
363 | msgid "Error: Unknown radio type\n"
364 | msgstr ""
365 |
366 | #: src/ofdm-transfer.c:1144
367 | #, c-format
368 | msgid "Info: Using IO pseudo-radio\n"
369 | msgstr ""
370 |
371 | #: src/ofdm-transfer.c:1151
372 | #, c-format
373 | msgid "Info: Using FILENAME pseudo-radio\n"
374 | msgstr ""
375 |
376 | #: src/ofdm-transfer.c:1199
377 | #, c-format
378 | msgid " No radio detected\n"
379 | msgstr ""
380 |
--------------------------------------------------------------------------------
/src/gettext.h:
--------------------------------------------------------------------------------
1 | /* Convenience header for conditional use of GNU .
2 | Copyright (C) 1995-1998, 2000-2002, 2004-2006, 2009-2020 Free Software
3 | Foundation, Inc.
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU Lesser General Public License as published by
7 | the Free Software Foundation; either version 2.1 of the License, or
8 | (at your option) any later version.
9 |
10 | This program is distributed in the hope that it will be useful,
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | GNU Lesser General Public License for more details.
14 |
15 | You should have received a copy of the GNU Lesser General Public License
16 | along with this program. If not, see . */
17 |
18 | #ifndef _LIBGETTEXT_H
19 | #define _LIBGETTEXT_H 1
20 |
21 | /* NLS can be disabled through the configure --disable-nls option
22 | or through "#define ENABLE NLS 0" before including this file. */
23 | #if defined ENABLE_NLS && ENABLE_NLS
24 |
25 | /* Get declarations of GNU message catalog functions. */
26 | # include
27 |
28 | /* You can set the DEFAULT_TEXT_DOMAIN macro to specify the domain used by
29 | the gettext() and ngettext() macros. This is an alternative to calling
30 | textdomain(), and is useful for libraries. */
31 | # ifdef DEFAULT_TEXT_DOMAIN
32 | # undef gettext
33 | # define gettext(Msgid) \
34 | dgettext (DEFAULT_TEXT_DOMAIN, Msgid)
35 | # undef ngettext
36 | # define ngettext(Msgid1, Msgid2, N) \
37 | dngettext (DEFAULT_TEXT_DOMAIN, Msgid1, Msgid2, N)
38 | # endif
39 |
40 | #else
41 |
42 | /* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
43 | chokes if dcgettext is defined as a macro. So include it now, to make
44 | later inclusions of a NOP. We don't include
45 | as well because people using "gettext.h" will not include ,
46 | and also including would fail on SunOS 4, whereas
47 | is OK. */
48 | #if defined(__sun)
49 | # include
50 | #endif
51 |
52 | /* Many header files from the libstdc++ coming with g++ 3.3 or newer include
53 | , which chokes if dcgettext is defined as a macro. So include
54 | it now, to make later inclusions of a NOP. */
55 | #if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3)
56 | # include
57 | # if (__GLIBC__ >= 2 && !defined __UCLIBC__) || _GLIBCXX_HAVE_LIBINTL_H
58 | # include
59 | # endif
60 | #endif
61 |
62 | /* Disabled NLS.
63 | The casts to 'const char *' serve the purpose of producing warnings
64 | for invalid uses of the value returned from these functions.
65 | On pre-ANSI systems without 'const', the config.h file is supposed to
66 | contain "#define const". */
67 | # undef gettext
68 | # define gettext(Msgid) ((const char *) (Msgid))
69 | # undef dgettext
70 | # define dgettext(Domainname, Msgid) ((void) (Domainname), gettext (Msgid))
71 | # undef dcgettext
72 | # define dcgettext(Domainname, Msgid, Category) \
73 | ((void) (Category), dgettext (Domainname, Msgid))
74 | # undef ngettext
75 | # define ngettext(Msgid1, Msgid2, N) \
76 | ((N) == 1 \
77 | ? ((void) (Msgid2), (const char *) (Msgid1)) \
78 | : ((void) (Msgid1), (const char *) (Msgid2)))
79 | # undef dngettext
80 | # define dngettext(Domainname, Msgid1, Msgid2, N) \
81 | ((void) (Domainname), ngettext (Msgid1, Msgid2, N))
82 | # undef dcngettext
83 | # define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
84 | ((void) (Category), dngettext (Domainname, Msgid1, Msgid2, N))
85 | # undef textdomain
86 | # define textdomain(Domainname) ((const char *) (Domainname))
87 | # undef bindtextdomain
88 | # define bindtextdomain(Domainname, Dirname) \
89 | ((void) (Domainname), (const char *) (Dirname))
90 | # undef bind_textdomain_codeset
91 | # define bind_textdomain_codeset(Domainname, Codeset) \
92 | ((void) (Domainname), (const char *) (Codeset))
93 |
94 | #endif
95 |
96 | /* Prefer gnulib's setlocale override over libintl's setlocale override. */
97 | #ifdef GNULIB_defined_setlocale
98 | # undef setlocale
99 | # define setlocale rpl_setlocale
100 | #endif
101 |
102 | /* A pseudo function call that serves as a marker for the automated
103 | extraction of messages, but does not call gettext(). The run-time
104 | translation is done at a different place in the code.
105 | The argument, String, should be a literal string. Concatenated strings
106 | and other string expressions won't work.
107 | The macro's expansion is not parenthesized, so that it is suitable as
108 | initializer for static 'char[]' or 'const char[]' variables. */
109 | #define gettext_noop(String) String
110 |
111 | /* The separator between msgctxt and msgid in a .mo file. */
112 | #define GETTEXT_CONTEXT_GLUE "\004"
113 |
114 | /* Pseudo function calls, taking a MSGCTXT and a MSGID instead of just a
115 | MSGID. MSGCTXT and MSGID must be string literals. MSGCTXT should be
116 | short and rarely need to change.
117 | The letter 'p' stands for 'particular' or 'special'. */
118 | #ifdef DEFAULT_TEXT_DOMAIN
119 | # define pgettext(Msgctxt, Msgid) \
120 | pgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES)
121 | #else
122 | # define pgettext(Msgctxt, Msgid) \
123 | pgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES)
124 | #endif
125 | #define dpgettext(Domainname, Msgctxt, Msgid) \
126 | pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, LC_MESSAGES)
127 | #define dcpgettext(Domainname, Msgctxt, Msgid, Category) \
128 | pgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, Category)
129 | #ifdef DEFAULT_TEXT_DOMAIN
130 | # define npgettext(Msgctxt, Msgid, MsgidPlural, N) \
131 | npgettext_aux (DEFAULT_TEXT_DOMAIN, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES)
132 | #else
133 | # define npgettext(Msgctxt, Msgid, MsgidPlural, N) \
134 | npgettext_aux (NULL, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES)
135 | #endif
136 | #define dnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N) \
137 | npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, LC_MESSAGES)
138 | #define dcnpgettext(Domainname, Msgctxt, Msgid, MsgidPlural, N, Category) \
139 | npgettext_aux (Domainname, Msgctxt GETTEXT_CONTEXT_GLUE Msgid, Msgid, MsgidPlural, N, Category)
140 |
141 | #ifdef __GNUC__
142 | __inline
143 | #else
144 | #ifdef __cplusplus
145 | inline
146 | #endif
147 | #endif
148 | static const char *
149 | pgettext_aux (const char *domain,
150 | const char *msg_ctxt_id, const char *msgid,
151 | int category)
152 | {
153 | const char *translation = dcgettext (domain, msg_ctxt_id, category);
154 | if (translation == msg_ctxt_id)
155 | return msgid;
156 | else
157 | return translation;
158 | }
159 |
160 | #ifdef __GNUC__
161 | __inline
162 | #else
163 | #ifdef __cplusplus
164 | inline
165 | #endif
166 | #endif
167 | static const char *
168 | npgettext_aux (const char *domain,
169 | const char *msg_ctxt_id, const char *msgid,
170 | const char *msgid_plural, unsigned long int n,
171 | int category)
172 | {
173 | const char *translation =
174 | dcngettext (domain, msg_ctxt_id, msgid_plural, n, category);
175 | if (translation == msg_ctxt_id || translation == msgid_plural)
176 | return (n == 1 ? msgid : msgid_plural);
177 | else
178 | return translation;
179 | }
180 |
181 | /* The same thing extended for non-constant arguments. Here MSGCTXT and MSGID
182 | can be arbitrary expressions. But for string literals these macros are
183 | less efficient than those above. */
184 |
185 | #include
186 |
187 | /* GNULIB_NO_VLA can be defined to disable use of VLAs even if supported.
188 | This relates to the -Wvla and -Wvla-larger-than warnings, enabled in
189 | the default GCC many warnings set. This allows programs to disable use
190 | of VLAs, which may be unintended, or may be awkward to support portably,
191 | or may have security implications due to non-deterministic stack usage. */
192 |
193 | #if (!defined GNULIB_NO_VLA \
194 | && (((__GNUC__ >= 3 || __GNUG__ >= 2) && !defined __STRICT_ANSI__) \
195 | /* || (__STDC_VERSION__ == 199901L && !defined __HP_cc)
196 | || (__STDC_VERSION__ >= 201112L && !defined __STDC_NO_VLA__) */ ))
197 | # define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS 1
198 | #else
199 | # define _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS 0
200 | #endif
201 |
202 | #if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
203 | #include
204 | #endif
205 |
206 | #define pgettext_expr(Msgctxt, Msgid) \
207 | dcpgettext_expr (NULL, Msgctxt, Msgid, LC_MESSAGES)
208 | #define dpgettext_expr(Domainname, Msgctxt, Msgid) \
209 | dcpgettext_expr (Domainname, Msgctxt, Msgid, LC_MESSAGES)
210 |
211 | #ifdef __GNUC__
212 | __inline
213 | #else
214 | #ifdef __cplusplus
215 | inline
216 | #endif
217 | #endif
218 | static const char *
219 | dcpgettext_expr (const char *domain,
220 | const char *msgctxt, const char *msgid,
221 | int category)
222 | {
223 | size_t msgctxt_len = strlen (msgctxt) + 1;
224 | size_t msgid_len = strlen (msgid) + 1;
225 | const char *translation;
226 | #if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
227 | char msg_ctxt_id[msgctxt_len + msgid_len];
228 | #else
229 | char buf[1024];
230 | char *msg_ctxt_id =
231 | (msgctxt_len + msgid_len <= sizeof (buf)
232 | ? buf
233 | : (char *) malloc (msgctxt_len + msgid_len));
234 | if (msg_ctxt_id != NULL)
235 | #endif
236 | {
237 | int found_translation;
238 | memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
239 | msg_ctxt_id[msgctxt_len - 1] = '\004';
240 | memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
241 | translation = dcgettext (domain, msg_ctxt_id, category);
242 | found_translation = (translation != msg_ctxt_id);
243 | #if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
244 | if (msg_ctxt_id != buf)
245 | free (msg_ctxt_id);
246 | #endif
247 | if (found_translation)
248 | return translation;
249 | }
250 | return msgid;
251 | }
252 |
253 | #define npgettext_expr(Msgctxt, Msgid, MsgidPlural, N) \
254 | dcnpgettext_expr (NULL, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES)
255 | #define dnpgettext_expr(Domainname, Msgctxt, Msgid, MsgidPlural, N) \
256 | dcnpgettext_expr (Domainname, Msgctxt, Msgid, MsgidPlural, N, LC_MESSAGES)
257 |
258 | #ifdef __GNUC__
259 | __inline
260 | #else
261 | #ifdef __cplusplus
262 | inline
263 | #endif
264 | #endif
265 | static const char *
266 | dcnpgettext_expr (const char *domain,
267 | const char *msgctxt, const char *msgid,
268 | const char *msgid_plural, unsigned long int n,
269 | int category)
270 | {
271 | size_t msgctxt_len = strlen (msgctxt) + 1;
272 | size_t msgid_len = strlen (msgid) + 1;
273 | const char *translation;
274 | #if _LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
275 | char msg_ctxt_id[msgctxt_len + msgid_len];
276 | #else
277 | char buf[1024];
278 | char *msg_ctxt_id =
279 | (msgctxt_len + msgid_len <= sizeof (buf)
280 | ? buf
281 | : (char *) malloc (msgctxt_len + msgid_len));
282 | if (msg_ctxt_id != NULL)
283 | #endif
284 | {
285 | int found_translation;
286 | memcpy (msg_ctxt_id, msgctxt, msgctxt_len - 1);
287 | msg_ctxt_id[msgctxt_len - 1] = '\004';
288 | memcpy (msg_ctxt_id + msgctxt_len, msgid, msgid_len);
289 | translation = dcngettext (domain, msg_ctxt_id, msgid_plural, n, category);
290 | found_translation = !(translation == msg_ctxt_id || translation == msgid_plural);
291 | #if !_LIBGETTEXT_HAVE_VARIABLE_SIZE_ARRAYS
292 | if (msg_ctxt_id != buf)
293 | free (msg_ctxt_id);
294 | #endif
295 | if (found_translation)
296 | return translation;
297 | }
298 | return (n == 1 ? msgid : msgid_plural);
299 | }
300 |
301 | #endif /* _LIBGETTEXT_H */
302 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of ofdm-transfer, a program to send or receive data
3 | by software defined radio using the OFDM modulation.
4 |
5 | Copyright 2021-2022 Guillaume LE VAILLANT
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include "gettext.h"
29 | #include "ofdm-transfer.h"
30 |
31 | #define _(string) gettext(string)
32 |
33 | void signal_handler(int signum)
34 | {
35 | if(ofdm_transfer_is_verbose())
36 | {
37 | fprintf(stderr, _("\nStopping (signal %d)\n"), signum);
38 | }
39 | else
40 | {
41 | fprintf(stderr, "\n");
42 | }
43 | ofdm_transfer_stop_all();
44 | }
45 |
46 | void usage()
47 | {
48 | printf(_("ofdm-transfer version 1.8.0\n"));
49 | printf("\n");
50 | printf(_("Usage: ofdm-transfer [options] [filename]\n"));
51 | printf("\n");
52 | printf(_("Options:\n"));
53 | printf(" -a\n");
54 | printf(_(" Use audio samples instead of IQ samples.\n"));
55 | printf(_(" -b (default: 38400 b/s)\n"));
56 | printf(_(" Bit rate of the OFDM transmission.\n"));
57 | printf(_(" -c (default: 0.0, can be negative)\n"));
58 | printf(_(" Correction for the radio clock.\n"));
59 | printf(_(" -d \n"));
60 | printf(_(" Dump a copy of the samples sent to or received from\n"
61 | " the radio.\n"));
62 | printf(_(" -e (default: h128,none)\n"));
63 | printf(_(" Inner and outer forward error correction codes to use.\n"));
64 | printf(_(" -f (default: 434000000 Hz)\n"));
65 | printf(_(" Frequency of the OFDM transmission.\n"));
66 | printf(_(" -g (default: 0)\n"));
67 | printf(_(" Gain of the radio transceiver, or audio gain in dB.\n"));
68 | printf(" -h\n");
69 | printf(_(" This help.\n"));
70 | printf(_(" -i (default: \"\")\n"));
71 | printf(_(" Transfer id (at most 4 bytes). When receiving, the frames\n"
72 | " with a different id will be ignored.\n"));
73 | printf(_(" -m (default: qpsk)\n"));
74 | printf(_(" Modulation to use for the subcarriers.\n"));
75 | printf(_(" -n (default: 64,16,4)\n"));
76 | printf(_(" Number of subcarriers, cyclic prefix length and taper length\n"
77 | " of the OFDM transmission.\n"));
78 | printf(_(" -o (default: 0 Hz, can be negative)\n"));
79 | printf(_(" Set the central frequency of the transceiver 'offset' Hz\n"
80 | " lower than the signal frequency to send or receive.\n"));
81 | printf(_(" -r (default: \"\")\n"));
82 | printf(_(" Radio to use.\n"));
83 | printf(_(" -s (default: 2000000 S/s)\n"));
84 | printf(_(" Sample rate to use.\n"));
85 | printf(_(" -T (default: 0 s)\n"));
86 | printf(_(" Number of seconds after which reception will be stopped if\n"
87 | " no frame has been received. A timeout of 0 means no timeout.\n"));
88 | printf(" -t\n");
89 | printf(_(" Use transmit mode.\n"));
90 | printf(" -v\n");
91 | printf(_(" Print debug messages.\n"));
92 | printf(_(" -w (default: 0.0 s)\n"));
93 | printf(_(" Wait a little before switching the radio off.\n"
94 | " This can be useful if the hardware needs some time to send\n"
95 | " the last samples it has buffered.\n"));
96 | printf("\n");
97 | printf(_("By default the program is in 'receive' mode.\n"
98 | "Use the '-t' option to use the 'transmit' mode.\n"));
99 | printf("\n");
100 | printf(_("In 'receive' mode, the samples are received from the radio,\n"
101 | "and the decoded data is written either to 'filename' if it\n"
102 | "is specified, or to standard output.\n"
103 | "In 'transmit' mode, the data to send is read either from\n"
104 | "'filename' if it is specified, or from standard input,\n"
105 | "and the samples are sent to the radio.\n"));
106 | printf("\n");
107 | printf(_("Instead of a real radio transceiver, the 'io' radio type uses\n"
108 | "standard input in 'receive' mode, and standard output in\n"
109 | "'transmit' mode.\n"
110 | "The 'file=path-to-file' radio type reads/writes the samples\n"
111 | "from/to 'path-to-file'.\n"
112 | "The IQ samples must be in 'complex float' format\n"
113 | "(32 bits for the real part, 32 bits for the imaginary part).\n"
114 | "The audio samples must be in 'signed integer' format (16 bits).\n"));
115 | printf("\n");
116 | printf(_("The gain parameter can be specified either as an integer to set a\n"
117 | "global gain, or as a series of keys and values to set specific\n"
118 | "gains (for example 'LNA=32,VGA=20').\n"
119 | "When using the audio mode (with the '-a' option), the gain value\n"
120 | "in dB is applied to the audio samples.\n"));
121 | printf("\n");
122 | printf(_("Available radios (via SoapySDR):\n"));
123 | ofdm_transfer_print_available_radios();
124 | printf("\n");
125 | printf(_("Available subcarrier modulations:\n"));
126 | ofdm_transfer_print_available_subcarrier_modulations();
127 | printf("\n");
128 | printf(_("Available forward error correction codes:\n"));
129 | ofdm_transfer_print_available_forward_error_codes();
130 | }
131 |
132 | void get_fec_schemes(char *str, char *inner_fec, char *outer_fec)
133 | {
134 | unsigned int size = strlen(str);
135 | char spec[size + 1];
136 | char *separation;
137 |
138 | strcpy(spec, str);
139 | if((separation = strchr(spec, ',')) != NULL)
140 | {
141 | *separation = '\0';
142 | }
143 |
144 | if(strlen(spec) < 32)
145 | {
146 | strcpy(inner_fec, spec);
147 | }
148 | else
149 | {
150 | strcpy(inner_fec, "unknown");
151 | }
152 |
153 | if(separation != NULL)
154 | {
155 | if(strlen(separation + 1) < 32)
156 | {
157 | strcpy(outer_fec, separation + 1);
158 | }
159 | else
160 | {
161 | strcpy(outer_fec, "unknown");
162 | }
163 | }
164 | else
165 | {
166 | strcpy(outer_fec, "none");
167 | }
168 | }
169 |
170 | void get_ofdm_configuration(char *str,
171 | unsigned int *subcarriers,
172 | unsigned int *cyclic_prefix_length,
173 | unsigned int *taper_length)
174 | {
175 | unsigned int size = strlen(str);
176 | char spec[size + 1];
177 | char *separation1 = NULL;
178 | char *separation2 = NULL;
179 |
180 | strcpy(spec, str);
181 | if((separation1 = strchr(spec, ',')) != NULL)
182 | {
183 | *separation1 = '\0';
184 | if((separation2 = strchr(separation1 + 1, ',')) != NULL)
185 | {
186 | *separation2 = '\0';
187 | }
188 | }
189 |
190 | *subcarriers = strtoul(spec, NULL, 10);
191 |
192 | if(separation1 != NULL)
193 | {
194 | *cyclic_prefix_length = strtoul(separation1 + 1, NULL, 10);
195 | }
196 | else
197 | {
198 | *cyclic_prefix_length = *subcarriers / 4;
199 | }
200 |
201 | if(separation2 != NULL)
202 | {
203 | *taper_length = strtoul(separation2 + 1, NULL, 10);
204 | }
205 | else
206 | {
207 | *taper_length = *cyclic_prefix_length / 4;
208 | }
209 | }
210 |
211 | int main(int argc, char **argv)
212 | {
213 | ofdm_transfer_t transfer;
214 | char *radio_driver = "";
215 | unsigned int emit = 0;
216 | unsigned long int sample_rate = 2000000;
217 | unsigned int bit_rate = 38400;
218 | unsigned long int frequency = 434000000;
219 | long int frequency_offset = 0;
220 | char *gain = "0";
221 | float ppm = 0;
222 | char *subcarrier_modulation = "qpsk";
223 | unsigned int subcarriers = 64;
224 | unsigned int cyclic_prefix_length = 16;
225 | unsigned int taper_length = 4;
226 | char inner_fec[32];
227 | char outer_fec[32];
228 | char *id = "";
229 | char *file = NULL;
230 | char *dump = NULL;
231 | float final_delay = 0;
232 | unsigned int final_delay_sec = 0;
233 | unsigned int final_delay_usec = 0;
234 | unsigned int timeout = 0;
235 | unsigned char audio = 0;
236 | int opt;
237 |
238 | strcpy(inner_fec, "h128");
239 | strcpy(outer_fec, "none");
240 |
241 | setlocale(LC_ALL, "");
242 | setlocale(LC_NUMERIC, "C");
243 | bindtextdomain(PACKAGE, LOCALEDIR);
244 | textdomain(PACKAGE);
245 |
246 | while((opt = getopt(argc, argv, "ab:c:d:e:f:g:hi:m:n:o:r:s:T:tvw:")) != -1)
247 | {
248 | switch(opt)
249 | {
250 | case 'a':
251 | audio = 1;
252 | break;
253 |
254 | case 'b':
255 | bit_rate = strtoul(optarg, NULL, 10);
256 | break;
257 |
258 | case 'c':
259 | ppm = strtof(optarg, NULL);
260 | break;
261 |
262 | case 'd':
263 | dump = optarg;
264 | break;
265 |
266 | case 'e':
267 | get_fec_schemes(optarg, inner_fec, outer_fec);
268 | break;
269 |
270 | case 'f':
271 | frequency = strtoul(optarg, NULL, 10);
272 | break;
273 |
274 | case 'g':
275 | gain = optarg;
276 | break;
277 |
278 | case 'h':
279 | usage();
280 | return(EXIT_SUCCESS);
281 |
282 | case 'i':
283 | id = optarg;
284 | break;
285 |
286 | case 'm':
287 | subcarrier_modulation = optarg;
288 | break;
289 |
290 | case 'n':
291 | get_ofdm_configuration(optarg,
292 | &subcarriers,
293 | &cyclic_prefix_length,
294 | &taper_length);
295 | break;
296 |
297 | case 'o':
298 | frequency_offset = strtol(optarg, NULL, 10);
299 | break;
300 |
301 | case 'r':
302 | radio_driver = optarg;
303 | break;
304 |
305 | case 's':
306 | sample_rate = strtoul(optarg, NULL, 10);
307 | break;
308 |
309 | case 't':
310 | emit = 1;
311 | break;
312 |
313 | case 'T':
314 | timeout = strtoul(optarg, NULL, 10);
315 | break;
316 |
317 | case 'v':
318 | ofdm_transfer_set_verbose(1);
319 | break;
320 |
321 | case 'w':
322 | final_delay = strtof(optarg, NULL);
323 | break;
324 |
325 | default:
326 | fprintf(stderr, _("Error: Unknown parameter: '-%c %s'\n"), opt, optarg);
327 | return(EXIT_FAILURE);
328 | }
329 | }
330 | if(optind < argc)
331 | {
332 | file = argv[optind];
333 | }
334 | else
335 | {
336 | file = NULL;
337 | }
338 |
339 | signal(SIGINT, &signal_handler);
340 | signal(SIGTERM, &signal_handler);
341 | signal(SIGABRT, &signal_handler);
342 |
343 | transfer = ofdm_transfer_create(radio_driver,
344 | emit,
345 | file,
346 | sample_rate,
347 | bit_rate,
348 | frequency,
349 | frequency_offset,
350 | gain,
351 | ppm,
352 | subcarrier_modulation,
353 | subcarriers,
354 | cyclic_prefix_length,
355 | taper_length,
356 | inner_fec,
357 | outer_fec,
358 | id,
359 | dump,
360 | timeout,
361 | audio);
362 | if(transfer == NULL)
363 | {
364 | fprintf(stderr, _("Error: Failed to initialize transfer\n"));
365 | return(EXIT_FAILURE);
366 | }
367 | ofdm_transfer_start(transfer);
368 | if(final_delay > 0)
369 | {
370 | /* Give enough time to the hardware to send the last samples */
371 | final_delay_sec = floorf(final_delay);
372 | final_delay_usec = (final_delay - final_delay_sec) * 1000000;
373 | if(final_delay_sec > 0)
374 | {
375 | sleep(final_delay_sec);
376 | }
377 | if(final_delay_usec > 0)
378 | {
379 | usleep(final_delay_usec);
380 | }
381 | }
382 | ofdm_transfer_free(transfer);
383 |
384 | if(ofdm_transfer_is_verbose())
385 | {
386 | fprintf(stderr, "\n");
387 | }
388 |
389 | return(EXIT_SUCCESS);
390 | }
391 |
--------------------------------------------------------------------------------
/po/fr.po:
--------------------------------------------------------------------------------
1 | # French translations for ofdm-transfer package.
2 | # Copyright (C) 2022 Guillaume LE VAILLANT
3 | # This file is distributed under the same license as the ofdm-transfer package.
4 | # Guillaume Le Vaillant , 2022.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: ofdm-transfer 1.8.0\n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2022-11-28 11:19+0100\n"
11 | "PO-Revision-Date: 2022-11-28 10:23+0100\n"
12 | "Last-Translator: Guillaume Le Vaillant \n"
13 | "Language-Team: French \n"
14 | "Language: fr\n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=2; plural=(n > 1);\n"
19 |
20 | #: src/main.c:37
21 | #, c-format
22 | msgid ""
23 | "\n"
24 | "Stopping (signal %d)\n"
25 | msgstr ""
26 | "\n"
27 | "Arrêt (signal %d)\n"
28 |
29 | #: src/main.c:48
30 | #, c-format
31 | msgid "ofdm-transfer version 1.8.0\n"
32 | msgstr "ofdm-transfer version 1.8.0\n"
33 |
34 | #: src/main.c:50
35 | #, c-format
36 | msgid "Usage: ofdm-transfer [options] [filename]\n"
37 | msgstr "Usage: ofdm-transfer [options] [fichier]\n"
38 |
39 | #: src/main.c:52
40 | #, c-format
41 | msgid "Options:\n"
42 | msgstr "Options :\n"
43 |
44 | #: src/main.c:54
45 | #, c-format
46 | msgid " Use audio samples instead of IQ samples.\n"
47 | msgstr " Utiliser des échantillons audio au lieu d'échantillons IQ.\n"
48 |
49 | #: src/main.c:55
50 | #, c-format
51 | msgid " -b (default: 38400 b/s)\n"
52 | msgstr " -b (défaut: 38400 b/s)\n"
53 |
54 | #: src/main.c:56
55 | #, c-format
56 | msgid " Bit rate of the OFDM transmission.\n"
57 | msgstr " Débit binaire de la transmission OFDM.\n"
58 |
59 | #: src/main.c:57
60 | #, c-format
61 | msgid " -c (default: 0.0, can be negative)\n"
62 | msgstr " -c (défaut: 0.0, peut être négative)\n"
63 |
64 | #: src/main.c:58
65 | #, c-format
66 | msgid " Correction for the radio clock.\n"
67 | msgstr " Correction pour l'horloge de la radio.\n"
68 |
69 | #: src/main.c:59
70 | #, c-format
71 | msgid " -d \n"
72 | msgstr " -d \n"
73 |
74 | #: src/main.c:60
75 | #, c-format
76 | msgid ""
77 | " Dump a copy of the samples sent to or received from\n"
78 | " the radio.\n"
79 | msgstr ""
80 | " Enregistrer une copie des échantillons envoyés à ou\n"
81 | " reçus de la radio.\n"
82 |
83 | #: src/main.c:62
84 | #, c-format
85 | msgid " -e (default: h128,none)\n"
86 | msgstr " -e (défaut: h128,none)\n"
87 |
88 | #: src/main.c:63
89 | #, c-format
90 | msgid " Inner and outer forward error correction codes to use.\n"
91 | msgstr " Codes de correction d'erreurs interne et externe à utiliser.\n"
92 |
93 | #: src/main.c:64
94 | #, c-format
95 | msgid " -f (default: 434000000 Hz)\n"
96 | msgstr " -f (défaut: 434000000 Hz)\n"
97 |
98 | #: src/main.c:65
99 | #, c-format
100 | msgid " Frequency of the OFDM transmission.\n"
101 | msgstr " Fréquence de la transmission OFDM.\n"
102 |
103 | #: src/main.c:66
104 | #, c-format
105 | msgid " -g (default: 0)\n"
106 | msgstr " -g (défaut: 0)\n"
107 |
108 | #: src/main.c:67
109 | #, c-format
110 | msgid " Gain of the radio transceiver, or audio gain in dB.\n"
111 | msgstr " Gain de l'émetteur-récepteur radio, ou gain audio en dB.\n"
112 |
113 | #: src/main.c:69
114 | #, c-format
115 | msgid " This help.\n"
116 | msgstr " Cette aide.\n"
117 |
118 | #: src/main.c:70
119 | #, c-format
120 | msgid " -i (default: \"\")\n"
121 | msgstr " -i (défaut: \"\")\n"
122 |
123 | #: src/main.c:71
124 | #, c-format
125 | msgid ""
126 | " Transfer id (at most 4 bytes). When receiving, the frames\n"
127 | " with a different id will be ignored.\n"
128 | msgstr ""
129 | " Id du transfert (au plus 4 octets). Lors de la réception,\n"
130 | " les trames avec un id différent seront ignorées.\n"
131 |
132 | #: src/main.c:73
133 | #, c-format
134 | msgid " -m (default: qpsk)\n"
135 | msgstr " -m (défaut: qpsk)\n"
136 |
137 | #: src/main.c:74
138 | #, c-format
139 | msgid " Modulation to use for the subcarriers.\n"
140 | msgstr " Modulation à utiliser pour les sous-porteuses.\n"
141 |
142 | #: src/main.c:75
143 | #, c-format
144 | msgid " -n (default: 64,16,4)\n"
145 | msgstr ""
146 | " -n (défaut: 64,16,4)\n"
147 |
148 | #: src/main.c:76
149 | #, c-format
150 | msgid ""
151 | " Number of subcarriers, cyclic prefix length and taper length\n"
152 | " of the OFDM transmission.\n"
153 | msgstr ""
154 | " Nombre de sous-porteuses, longueur du préfixe cyclique et longueur\n"
155 | " d'effilage de la transmission OFDM.\n"
156 |
157 | #: src/main.c:78
158 | #, c-format
159 | msgid " -o (default: 0 Hz, can be negative)\n"
160 | msgstr " -o (défaut: 0 Hz, peut être négatif)\n"
161 |
162 | #: src/main.c:79
163 | #, c-format
164 | msgid ""
165 | " Set the central frequency of the transceiver 'offset' Hz\n"
166 | " lower than the signal frequency to send or receive.\n"
167 | msgstr ""
168 | " Mettre la fréquence centrale de l'émetteur-récepteur 'décalage' Hz\n"
169 | " plus bas que la fréquence du signal à envoyer ou recevoir.\n"
170 |
171 | #: src/main.c:81
172 | #, c-format
173 | msgid " -r (default: \"\")\n"
174 | msgstr " -r (défaut: \"\")\n"
175 |
176 | #: src/main.c:82
177 | #, c-format
178 | msgid " Radio to use.\n"
179 | msgstr " Radio à utiliser.\n"
180 |
181 | #: src/main.c:83
182 | #, c-format
183 | msgid " -s (default: 2000000 S/s)\n"
184 | msgstr " -s (défaut: 2000000 S/s)\n"
185 |
186 | #: src/main.c:84
187 | #, c-format
188 | msgid " Sample rate to use.\n"
189 | msgstr " Vitesse d'échantillonnage à utiliser.\n"
190 |
191 | #: src/main.c:85
192 | #, c-format
193 | msgid " -T (default: 0 s)\n"
194 | msgstr " -T (défaut: 0 s)\n"
195 |
196 | #: src/main.c:86
197 | #, c-format
198 | msgid ""
199 | " Number of seconds after which reception will be stopped if\n"
200 | " no frame has been received. A timeout of 0 means no timeout.\n"
201 | msgstr ""
202 | " Nombre de secondes après lesquelles la réception sera stoppée si\n"
203 | " aucune trame n'a été reçue.\n"
204 | " Une temporisation de 0 signifie aucune temporisation.\n"
205 |
206 | #: src/main.c:89
207 | #, c-format
208 | msgid " Use transmit mode.\n"
209 | msgstr " Utiliser le mode transmission.\n"
210 |
211 | #: src/main.c:91
212 | #, c-format
213 | msgid " Print debug messages.\n"
214 | msgstr " Afficher les messages de débogage.\n"
215 |
216 | #: src/main.c:92
217 | #, c-format
218 | msgid " -w (default: 0.0 s)\n"
219 | msgstr " -w (défaut: 0.0 s)\n"
220 |
221 | #: src/main.c:93
222 | #, c-format
223 | msgid ""
224 | " Wait a little before switching the radio off.\n"
225 | " This can be useful if the hardware needs some time to send\n"
226 | " the last samples it has buffered.\n"
227 | msgstr ""
228 | " Attendre un peu après avoir éteint la radio.\n"
229 | " Ceci peut être utile si le matériel a besoin de temps pour envoyer\n"
230 | " les derniers échantillons dans les tampons.\n"
231 |
232 | #: src/main.c:97
233 | #, c-format
234 | msgid ""
235 | "By default the program is in 'receive' mode.\n"
236 | "Use the '-t' option to use the 'transmit' mode.\n"
237 | msgstr ""
238 | "Par défaut le programme est en mode réception.\n"
239 | "Utilisez l'option '-t' pour utiliser le mode émission.\n"
240 |
241 | #: src/main.c:100
242 | #, c-format
243 | msgid ""
244 | "In 'receive' mode, the samples are received from the radio,\n"
245 | "and the decoded data is written either to 'filename' if it\n"
246 | "is specified, or to standard output.\n"
247 | "In 'transmit' mode, the data to send is read either from\n"
248 | "'filename' if it is specified, or from standard input,\n"
249 | "and the samples are sent to the radio.\n"
250 | msgstr ""
251 | "En mode réception, les échantillons sont reçus de la radio,\n"
252 | "et les données décodées sont écrites soit vers 'fichier' si il\n"
253 | "est spécifié, ou vers la sortie standard.\n"
254 | "En mode émission, les données à envoyer sont lues soit de\n"
255 | "'fichier' si il est spécifié, ou de l'entrée standard,\n"
256 | "et les échantillons sont envoyés à la radio.\n"
257 |
258 | #: src/main.c:107
259 | #, c-format
260 | msgid ""
261 | "Instead of a real radio transceiver, the 'io' radio type uses\n"
262 | "standard input in 'receive' mode, and standard output in\n"
263 | "'transmit' mode.\n"
264 | "The 'file=path-to-file' radio type reads/writes the samples\n"
265 | "from/to 'path-to-file'.\n"
266 | "The IQ samples must be in 'complex float' format\n"
267 | "(32 bits for the real part, 32 bits for the imaginary part).\n"
268 | "The audio samples must be in 'signed integer' format (16 bits).\n"
269 | msgstr ""
270 | "Au lieu d'un émetteur-récepteur radio réel, le type de radio 'io'\n"
271 | "utilise l'entrée standard en mode réception, et la sortie standard\n"
272 | "en mode émission.\n"
273 | "Le type de radio 'file=chemin-vers-fichier' lit/écrit les échantillons\n"
274 | "de/vers 'chemin-vers-fichier'.\n"
275 | "Les échantillons IQ doivent avoir le format 'complex float'\n"
276 | "(32 bits pour la partie réelle, 32 bits pour la partie imaginaire).\n"
277 | "Les échantillons audio doivent avoir le format 'signed integer' (16 bits).\n"
278 |
279 | #: src/main.c:116
280 | #, c-format
281 | msgid ""
282 | "The gain parameter can be specified either as an integer to set a\n"
283 | "global gain, or as a series of keys and values to set specific\n"
284 | "gains (for example 'LNA=32,VGA=20').\n"
285 | "When using the audio mode (with the '-a' option), the gain value\n"
286 | "in dB is applied to the audio samples.\n"
287 | msgstr ""
288 | "Le paramètre de gain peut être spécifié soit comme un entier pour définir\n"
289 | "un gain global, ou comme une série de clés et de valeurs pour définir des\n"
290 | "gains spécifiques (par exemple 'LNA=32,VGA=20').\n"
291 | "Lorsque le mode audio est utilisé (avec l'option '-a'), la valeur de gain\n"
292 | "en dB est appliquée aux échantillons audio.\n"
293 |
294 | #: src/main.c:122
295 | #, c-format
296 | msgid "Available radios (via SoapySDR):\n"
297 | msgstr "Radios disponibles (via SoapySDR) :\n"
298 |
299 | #: src/main.c:125
300 | #, c-format
301 | msgid "Available subcarrier modulations:\n"
302 | msgstr "Modulations de sous-porteuse disponibles :\n"
303 |
304 | #: src/main.c:128
305 | #, c-format
306 | msgid "Available forward error correction codes:\n"
307 | msgstr "Codes de correction d'erreurs disponibles :\n"
308 |
309 | #: src/main.c:326
310 | #, c-format
311 | msgid "Error: Unknown parameter: '-%c %s'\n"
312 | msgstr "Erreur : Paramètre inconnu : '-%c %s'\n"
313 |
314 | #: src/main.c:364
315 | #, c-format
316 | msgid "Error: Failed to initialize transfer\n"
317 | msgstr "Erreur : Échec de l'initialisation du transfert\n"
318 |
319 | #: src/ofdm-transfer.c:49 src/ofdm-transfer.c:947 src/ofdm-transfer.c:992
320 | #, c-format
321 | msgid "Error: %s\n"
322 | msgstr "Erreur : %s\n"
323 |
324 | #: src/ofdm-transfer.c:482 src/ofdm-transfer.c:657 src/ofdm-transfer.c:757
325 | #, c-format
326 | msgid "Error: Memory allocation failed\n"
327 | msgstr "Erreur: Échec de l'allocation de mémoire\n"
328 |
329 | #: src/ofdm-transfer.c:609
330 | #, c-format
331 | msgid "Frame %u for '%s': corrupted header\n"
332 | msgstr "Trame %u pour '%s' : en-tête corrompu\n"
333 |
334 | #: src/ofdm-transfer.c:613
335 | #, c-format
336 | msgid "Frame %u for '%s': corrupted payload\n"
337 | msgstr "Trame %u pour '%s' : charge utile corrompue\n"
338 |
339 | #: src/ofdm-transfer.c:622
340 | #, c-format
341 | msgid "Frame %u for '%s': ignored\n"
342 | msgstr "Trame %u pour '%s' : ignorée\n"
343 |
344 | #: src/ofdm-transfer.c:691
345 | #, c-format
346 | msgid "Timeout: %d s without frames\n"
347 | msgstr "Temporisation : %d s sans trames\n"
348 |
349 | #: src/ofdm-transfer.c:787
350 | #, c-format
351 | msgid "Error: Invalid sample rate\n"
352 | msgstr "Erreur : Vitesse d'échantillonnage non valide\n"
353 |
354 | #: src/ofdm-transfer.c:798
355 | #, c-format
356 | msgid "Error: Invalid frequency\n"
357 | msgstr "Erreur : Fréquence non valide\n"
358 |
359 | #: src/ofdm-transfer.c:821
360 | #, c-format
361 | msgid "Error: This radio type only supports IQ samples\n"
362 | msgstr "Erreur : Cette radio ne supporte que les échantillons IQ\n"
363 |
364 | #: src/ofdm-transfer.c:838
365 | #, c-format
366 | msgid "Error: Invalid bit rate\n"
367 | msgstr "Erreur : Débit non valide\n"
368 |
369 | #: src/ofdm-transfer.c:857
370 | #, c-format
371 | msgid "Error: Invalid subcarrier modulation\n"
372 | msgstr "Erreur : Modulation de sous-porteuse non valide\n"
373 |
374 | #: src/ofdm-transfer.c:868
375 | #, c-format
376 | msgid "Error: Invalid number of subcarriers\n"
377 | msgstr "Erreur : Nombre de sous-porteuses non valide\n"
378 |
379 | #: src/ofdm-transfer.c:880
380 | #, c-format
381 | msgid "Error: Invalid inner FEC\n"
382 | msgstr "Erreur : FEC interne non valide\n"
383 |
384 | #: src/ofdm-transfer.c:888
385 | #, c-format
386 | msgid "Error: Invalid outer FEC\n"
387 | msgstr "Erreur : FEC externe non valide\n"
388 |
389 | #: src/ofdm-transfer.c:899
390 | #, c-format
391 | msgid "Error: Id must be at most 4 bytes long\n"
392 | msgstr "Erreur : Id doit être long d'au plus 4 octets\n"
393 |
394 | #: src/ofdm-transfer.c:909 src/ofdm-transfer.c:937 src/ofdm-transfer.c:1070
395 | #, c-format
396 | msgid "Error: Failed to open '%s'\n"
397 | msgstr "Erreur : Échec de l'ouverture de '%s'\n"
398 |
399 | #: src/ofdm-transfer.c:1000
400 | #, c-format
401 | msgid "Error: Unknown radio type\n"
402 | msgstr "Erreur : Type de radio inconnu\n"
403 |
404 | #: src/ofdm-transfer.c:1144
405 | #, c-format
406 | msgid "Info: Using IO pseudo-radio\n"
407 | msgstr "Info : Utilisation de la pseudo-radio IO\n"
408 |
409 | #: src/ofdm-transfer.c:1151
410 | #, c-format
411 | msgid "Info: Using FILENAME pseudo-radio\n"
412 | msgstr "Info : Utilisation de la pseudo-radio FILENAME\n"
413 |
414 | #: src/ofdm-transfer.c:1199
415 | #, c-format
416 | msgid " No radio detected\n"
417 | msgstr " Pas de radio détectée\n"
418 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/src/ofdm-transfer.c:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of ofdm-transfer, a program to send or receive data
3 | by software defined radio using the OFDM modulation.
4 |
5 | Copyright 2021-2022 Guillaume LE VAILLANT
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 | */
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include "gettext.h"
35 | #include "ofdm-transfer.h"
36 |
37 | #define TAU (2 * M_PI)
38 |
39 | #define MIN(x, y) ((x < y) ? x : y)
40 | #define MAX(x, y) ((x > y) ? x : y)
41 |
42 | #define _(string) gettext(string)
43 |
44 | #define SOAPYSDR_CHECK(funcall) \
45 | { \
46 | int e = funcall; \
47 | if(e != 0) \
48 | { \
49 | fprintf(stderr, _("Error: %s\n"), SoapySDRDevice_lastError()); \
50 | exit(EXIT_FAILURE); \
51 | } \
52 | }
53 |
54 | typedef enum
55 | {
56 | IO,
57 | FILENAME,
58 | SOAPYSDR
59 | } radio_type_t;
60 |
61 | typedef union
62 | {
63 | FILE *file;
64 | SoapySDRDevice *soapysdr;
65 | } radio_device_t;
66 |
67 | typedef union
68 | {
69 | SoapySDRStream *soapysdr;
70 | } radio_stream_t;
71 |
72 | struct ofdm_transfer_s
73 | {
74 | radio_type_t radio_type;
75 | radio_device_t radio_device;
76 | radio_stream_t radio_stream;
77 | unsigned char emit;
78 | FILE *file;
79 | unsigned long int sample_rate;
80 | unsigned int bit_rate;
81 | unsigned long int frequency;
82 | long int frequency_offset;
83 | modulation_scheme subcarrier_modulation;
84 | unsigned int subcarriers;
85 | unsigned int cyclic_prefix_length;
86 | unsigned int taper_length;
87 | crc_scheme crc;
88 | fec_scheme inner_fec;
89 | fec_scheme outer_fec;
90 | char id[5];
91 | FILE *dump;
92 | unsigned char stop;
93 | int (*data_callback)(void *, unsigned char *, unsigned int);
94 | void *callback_context;
95 | unsigned int timeout;
96 | time_t timeout_start;
97 | firhilbf audio_converter;
98 | float audio_gain;
99 | };
100 |
101 | unsigned char stop = 0;
102 | unsigned char verbose = 0;
103 |
104 | void ofdm_transfer_set_verbose(unsigned char v)
105 | {
106 | verbose = v;
107 | }
108 |
109 | unsigned char ofdm_transfer_is_verbose()
110 | {
111 | return(verbose);
112 | }
113 |
114 | void dump_samples(ofdm_transfer_t transfer,
115 | complex float *samples,
116 | unsigned int samples_size)
117 | {
118 | fwrite(samples, sizeof(complex float), samples_size, transfer->dump);
119 | }
120 |
121 | int read_data(void *context,
122 | unsigned char *payload,
123 | unsigned int payload_size)
124 | {
125 | ofdm_transfer_t transfer = (ofdm_transfer_t) context;
126 | int n;
127 |
128 | if(feof(transfer->file))
129 | {
130 | return(-1);
131 | }
132 |
133 | n = fread(payload, 1, payload_size, transfer->file);
134 | if(n == 0)
135 | {
136 | usleep(1);
137 | }
138 |
139 | return(n);
140 | }
141 |
142 | int write_data(void *context,
143 | unsigned char *payload,
144 | unsigned int payload_size)
145 | {
146 | ofdm_transfer_t transfer = (ofdm_transfer_t) context;
147 |
148 | fwrite(payload, 1, payload_size, transfer->file);
149 | if(transfer->file == stdout)
150 | {
151 | fflush(transfer->file);
152 | }
153 |
154 | return(payload_size);
155 | }
156 |
157 | void write_audio(ofdm_transfer_t transfer,
158 | complex float *samples,
159 | unsigned int samples_size,
160 | FILE *output)
161 | {
162 | float gain = transfer->audio_gain;
163 | unsigned int n;
164 | float audio_samples[2];
165 | short int audio_samples_s16[2];
166 |
167 | for(n = 0; n < samples_size; n++)
168 | {
169 | firhilbf_interp_execute(transfer->audio_converter,
170 | samples[n],
171 | audio_samples);
172 | audio_samples_s16[0] = (audio_samples[0] * gain) * 32767;
173 | audio_samples_s16[1] = (audio_samples[1] * gain) * 32767;
174 | fwrite(audio_samples_s16, sizeof(short int), 2, output);
175 | }
176 | }
177 |
178 | unsigned int read_audio(ofdm_transfer_t transfer,
179 | complex float *samples,
180 | unsigned int samples_size,
181 | FILE* input)
182 | {
183 | float gain = transfer->audio_gain;
184 | unsigned int n = 0;
185 | float audio_samples[2];
186 | short int audio_samples_s16[2];
187 |
188 | while((n < samples_size) &&
189 | (fread(audio_samples_s16, sizeof(short int), 2, input) == 2))
190 | {
191 | audio_samples[0] = (audio_samples_s16[0] * gain) / 32768.0;
192 | audio_samples[1] = (audio_samples_s16[1] * gain) / 32768.0;
193 | firhilbf_decim_execute(transfer->audio_converter,
194 | audio_samples,
195 | &samples[n]);
196 | n++;
197 | }
198 | return(n);
199 | }
200 |
201 | void send_to_radio(ofdm_transfer_t transfer,
202 | complex float *samples,
203 | unsigned int samples_size,
204 | int last)
205 | {
206 | unsigned int n;
207 | unsigned int size;
208 | int flags = 0;
209 | size_t mask = 0;
210 | long long int timestamp = 0;
211 | int r;
212 | const void *buffers[1];
213 |
214 | if(transfer->dump)
215 | {
216 | dump_samples(transfer, samples, samples_size);
217 | }
218 |
219 | switch(transfer->radio_type)
220 | {
221 | case IO:
222 | if(transfer->audio_converter)
223 | {
224 | write_audio(transfer, samples, samples_size, stdout);
225 | }
226 | else
227 | {
228 | fwrite(samples, sizeof(complex float), samples_size, stdout);
229 | }
230 | break;
231 |
232 | case FILENAME:
233 | if(transfer->audio_converter)
234 | {
235 | write_audio(transfer, samples, samples_size, transfer->radio_device.file);
236 | }
237 | else
238 | {
239 | fwrite(samples,
240 | sizeof(complex float),
241 | samples_size,
242 | transfer->radio_device.file);
243 | }
244 | break;
245 |
246 | case SOAPYSDR:
247 | n = 0;
248 | while((n < samples_size) && (!stop) && (!transfer->stop))
249 | {
250 | buffers[0] = &samples[n];
251 | size = samples_size - n;
252 | r = SoapySDRDevice_writeStream(transfer->radio_device.soapysdr,
253 | transfer->radio_stream.soapysdr,
254 | buffers,
255 | size,
256 | &flags,
257 | 0,
258 | 10000);
259 | if(r > 0)
260 | {
261 | n += r;
262 | }
263 | }
264 | if(last)
265 | {
266 | /* Complete the remaining buffer to ensure that SoapySDR
267 | * will process it */
268 | flags = SOAPY_SDR_END_BURST;
269 | size = SoapySDRDevice_getStreamMTU(transfer->radio_device.soapysdr,
270 | transfer->radio_stream.soapysdr);
271 | bzero(samples, samples_size * sizeof(complex float));
272 | buffers[0] = samples;
273 | while((size > 0) && (!stop) && (!transfer->stop))
274 | {
275 | n = (samples_size < size) ? samples_size : size;
276 | r = SoapySDRDevice_writeStream(transfer->radio_device.soapysdr,
277 | transfer->radio_stream.soapysdr,
278 | buffers,
279 | n,
280 | &flags,
281 | 0,
282 | 10000);
283 | if(r > 0)
284 | {
285 | size -= r;
286 | }
287 | }
288 | do
289 | {
290 | r = SoapySDRDevice_readStreamStatus(transfer->radio_device.soapysdr,
291 | transfer->radio_stream.soapysdr,
292 | &mask,
293 | &flags,
294 | ×tamp,
295 | 10000);
296 | }
297 | while((r != SOAPY_SDR_UNDERFLOW) && (!stop) && (!transfer->stop));
298 | }
299 | break;
300 | }
301 | }
302 |
303 | unsigned int receive_from_radio(ofdm_transfer_t transfer,
304 | complex float *samples,
305 | unsigned int samples_size)
306 | {
307 | unsigned int n = 0;
308 | int flags;
309 | long long int timestamp;
310 | int r;
311 | void *buffers[1];
312 |
313 | switch(transfer->radio_type)
314 | {
315 | case IO:
316 | if(transfer->audio_converter)
317 | {
318 | n = read_audio(transfer, samples, samples_size, stdin);
319 | }
320 | else
321 | {
322 | n = fread(samples, sizeof(complex float), samples_size, stdin);
323 | }
324 | break;
325 |
326 | case FILENAME:
327 | if(transfer->audio_converter)
328 | {
329 | n = read_audio(transfer,
330 | samples,
331 | samples_size,
332 | transfer->radio_device.file);
333 | }
334 | else
335 | {
336 | n = fread(samples,
337 | sizeof(complex float),
338 | samples_size,
339 | transfer->radio_device.file);
340 | }
341 | break;
342 |
343 | case SOAPYSDR:
344 | buffers[0] = samples;
345 | r = SoapySDRDevice_readStream(transfer->radio_device.soapysdr,
346 | transfer->radio_stream.soapysdr,
347 | buffers,
348 | samples_size,
349 | &flags,
350 | ×tamp,
351 | 10000);
352 | if(r >= 0)
353 | {
354 | n = r;
355 | }
356 | break;
357 | }
358 | return(n);
359 | }
360 |
361 | unsigned int bits_per_symbol(modulation_scheme modulation)
362 | {
363 | unsigned int n;
364 |
365 | switch(modulation)
366 | {
367 | case LIQUID_MODEM_BPSK:
368 | n = 1;
369 | break;
370 |
371 | case LIQUID_MODEM_QPSK:
372 | n = 2;
373 | break;
374 |
375 | case LIQUID_MODEM_PSK8:
376 | n = 3;
377 | break;
378 |
379 | case LIQUID_MODEM_APSK16:
380 | n = 4;
381 | break;
382 |
383 | case LIQUID_MODEM_APSK32:
384 | n = 5;
385 | break;
386 |
387 | case LIQUID_MODEM_APSK64:
388 | n = 6;
389 | break;
390 |
391 | case LIQUID_MODEM_APSK128:
392 | n = 7;
393 | break;
394 |
395 | case LIQUID_MODEM_APSK256:
396 | n = 8;
397 | break;
398 |
399 | default:
400 | n = 1;
401 | break;
402 | }
403 | return(n);
404 | }
405 |
406 | void set_counter(unsigned char *header, unsigned int counter)
407 | {
408 | header[4] = (counter >> 24) & 255;
409 | header[5] = (counter >> 16) & 255;
410 | header[6] = (counter >> 8) & 255;
411 | header[7] = counter & 255;
412 | }
413 |
414 | unsigned int get_counter(unsigned char *header)
415 | {
416 | return((header[4] << 24) | (header[5] << 16) | (header[6] << 8) | header[7]);
417 | }
418 |
419 | void send_dummy_samples(ofdm_transfer_t transfer,
420 | msresamp_crcf resampler,
421 | nco_crcf oscillator,
422 | complex float *samples,
423 | unsigned int delay,
424 | int last)
425 | {
426 | unsigned int i;
427 | unsigned int n;
428 | complex float zero_sample = 0;
429 |
430 | for(i = 0; i < delay; i++)
431 | {
432 | msresamp_crcf_execute(resampler, &zero_sample, 1, samples, &n);
433 | if(transfer->frequency_offset != 0)
434 | {
435 | nco_crcf_mix_block_up(oscillator, samples, samples, n);
436 | }
437 | if(i + 1 < delay)
438 | {
439 | send_to_radio(transfer, samples, n, 0);
440 | }
441 | else
442 | {
443 | send_to_radio(transfer, samples, n, last);
444 | }
445 | }
446 | }
447 |
448 | void send_frames(ofdm_transfer_t transfer)
449 | {
450 | unsigned int subcarrier_symbol_bits = bits_per_symbol(transfer->subcarrier_modulation);
451 | float samples_per_bit = 2.0 / subcarrier_symbol_bits;
452 | ofdmflexframegenprops_s frame_properties;
453 | ofdmflexframegen frame_generator;
454 | float resampling_ratio = (float) transfer->sample_rate / (transfer->bit_rate *
455 | samples_per_bit);
456 | msresamp_crcf resampler = msresamp_crcf_create(resampling_ratio, 60);
457 | unsigned int delay = ceilf(msresamp_crcf_get_delay(resampler));
458 | unsigned int header_size = 8;
459 | unsigned char header[header_size];
460 | /* Try to make frames of approximately 100 ms, but containing at least
461 | * 16 bytes and at most 8000 bytes of payload */
462 | unsigned int byte_rate = transfer->bit_rate / 8;
463 | unsigned int payload_size = MIN(MAX(byte_rate * 0.1, 16), 8000);
464 | int r;
465 | unsigned int n;
466 | unsigned int i;
467 | /* Process data by blocks of 50 ms */
468 | unsigned int frame_samples_size = ceilf((transfer->bit_rate *
469 | samples_per_bit) / 20.0);
470 | unsigned int samples_size = ceilf((frame_samples_size + delay) * resampling_ratio);
471 | int frame_complete;
472 | float center_frequency = (float) transfer->frequency_offset / transfer->sample_rate;
473 | nco_crcf oscillator = nco_crcf_create(LIQUID_NCO);
474 | float maximum_amplitude = 1;
475 | unsigned int counter = 0;
476 | unsigned char *payload = malloc(payload_size);
477 | complex float *frame_samples = malloc(frame_samples_size * sizeof(complex float));
478 | complex float *samples = malloc(samples_size * sizeof(complex float));
479 |
480 | if((payload == NULL) || (frame_samples == NULL) || (samples == NULL))
481 | {
482 | fprintf(stderr, _("Error: Memory allocation failed\n"));
483 | exit(EXIT_FAILURE);
484 | }
485 |
486 | nco_crcf_set_phase(oscillator, 0);
487 | nco_crcf_set_frequency(oscillator, TAU * center_frequency);
488 |
489 | ofdmflexframegenprops_init_default(&frame_properties);
490 | frame_properties.check = transfer->crc;
491 | frame_properties.fec0 = transfer->inner_fec;
492 | frame_properties.fec1 = transfer->outer_fec;
493 | frame_properties.mod_scheme = transfer->subcarrier_modulation;
494 | frame_generator = ofdmflexframegen_create(transfer->subcarriers,
495 | transfer->cyclic_prefix_length,
496 | transfer->taper_length,
497 | NULL,
498 | &frame_properties);
499 | ofdmflexframegen_set_header_props(frame_generator, &frame_properties);
500 | ofdmflexframegen_set_header_len(frame_generator, header_size);
501 | memcpy(header, transfer->id, 4);
502 | set_counter(header, counter);
503 |
504 | while((!stop) && (!transfer->stop))
505 | {
506 | r = transfer->data_callback(transfer->callback_context, payload, payload_size);
507 | if(r < 0)
508 | {
509 | break;
510 | }
511 | n = r;
512 | if(n > 0)
513 | {
514 | ofdmflexframegen_assemble(frame_generator, header, payload, n);
515 | frame_complete = 0;
516 | while(!frame_complete)
517 | {
518 | frame_complete = ofdmflexframegen_write(frame_generator,
519 | frame_samples,
520 | frame_samples_size);
521 | n = frame_samples_size;
522 | if(frame_complete)
523 | {
524 | /* Don't send the padding 0 bytes */
525 | while((n > 0) && (frame_samples[n - 1] == 0))
526 | {
527 | n--;
528 | }
529 | }
530 | /* Reduce the amplitude of samples because the frame generator and
531 | * the resampler may produce samples with an amplitude greater than
532 | * 1.0 depending on the number of carriers and resampling ratio */
533 | maximum_amplitude = 1;
534 | for(i = 0; i < n; i++)
535 | {
536 | if(cabsf(frame_samples[i]) > maximum_amplitude)
537 | {
538 | maximum_amplitude = cabsf(frame_samples[i]);
539 | }
540 | }
541 | liquid_vectorcf_mulscalar(frame_samples,
542 | n,
543 | 0.75 / maximum_amplitude,
544 | frame_samples);
545 | msresamp_crcf_execute(resampler, frame_samples, n, samples, &n);
546 | if(transfer->frequency_offset != 0)
547 | {
548 | nco_crcf_mix_block_up(oscillator, samples, samples, n);
549 | }
550 | send_to_radio(transfer, samples, n, 0);
551 | }
552 | counter++;
553 | set_counter(header, counter);
554 | }
555 | else
556 | {
557 | /* Underrun when reading from stdin. Send some dummy samples to get the
558 | * remaining output samples for the end of current frame (because of
559 | * resampler and filter delays) and send them */
560 | send_dummy_samples(transfer,
561 | resampler,
562 | oscillator,
563 | samples,
564 | delay,
565 | 0);
566 | }
567 | }
568 |
569 | /* Send some dummy samples to get the remaining output samples (because of
570 | * resampler and filter delays) */
571 | send_dummy_samples(transfer,
572 | resampler,
573 | oscillator,
574 | samples,
575 | delay,
576 | 1);
577 |
578 | free(samples);
579 | free(frame_samples);
580 | free(payload);
581 | nco_crcf_destroy(oscillator);
582 | msresamp_crcf_destroy(resampler);
583 | ofdmflexframegen_destroy(frame_generator);
584 | }
585 |
586 | int frame_received(unsigned char *header,
587 | int header_valid,
588 | unsigned char *payload,
589 | unsigned int payload_size,
590 | int payload_valid,
591 | framesyncstats_s stats,
592 | void *user_data)
593 | {
594 | ofdm_transfer_t transfer = (ofdm_transfer_t) user_data;
595 | char id[5];
596 | unsigned int counter;
597 |
598 | transfer->timeout_start = time(NULL);
599 | memcpy(id, header, 4);
600 | id[4] = '\0';
601 | counter = get_counter(header);
602 |
603 | if(!header_valid || !payload_valid)
604 | {
605 | if(verbose)
606 | {
607 | if(!header_valid)
608 | {
609 | fprintf(stderr, _("Frame %u for '%s': corrupted header\n"), counter, id);
610 | }
611 | if(!payload_valid)
612 | {
613 | fprintf(stderr, _("Frame %u for '%s': corrupted payload\n"), counter, id);
614 | }
615 | fflush(stderr);
616 | }
617 | }
618 | else if(memcmp(id, transfer->id, 4) != 0)
619 | {
620 | if(verbose)
621 | {
622 | fprintf(stderr, _("Frame %u for '%s': ignored\n"), counter, id);
623 | fflush(stderr);
624 | }
625 | }
626 | else
627 | {
628 | transfer->data_callback(transfer->callback_context, payload, payload_size);
629 | }
630 | return(0);
631 | }
632 |
633 | void receive_frames(ofdm_transfer_t transfer)
634 | {
635 | unsigned int subcarrier_symbol_bits = bits_per_symbol(transfer->subcarrier_modulation);
636 | float samples_per_bit = 2.0 / subcarrier_symbol_bits;
637 | ofdmflexframegenprops_s frame_properties;
638 | ofdmflexframesync frame_synchronizer;
639 | float resampling_ratio = (transfer->bit_rate *
640 | samples_per_bit) / (float) transfer->sample_rate;
641 | msresamp_crcf resampler = msresamp_crcf_create(resampling_ratio, 60);
642 | unsigned int delay = ceilf(msresamp_crcf_get_delay(resampler));
643 | unsigned int header_size = 8;
644 | unsigned int n;
645 | /* Process data by blocks of 50 ms */
646 | unsigned int frame_samples_size = ceilf((transfer->bit_rate *
647 | samples_per_bit) / 20.0);
648 | unsigned int samples_size = floorf(frame_samples_size / resampling_ratio);
649 | nco_crcf oscillator = nco_crcf_create(LIQUID_NCO);
650 | complex float *frame_samples = malloc((frame_samples_size + delay) *
651 | sizeof(complex float));
652 | complex float *samples = malloc((samples_size + delay) *
653 | sizeof(complex float));
654 |
655 | if((frame_samples == NULL) || (samples == NULL))
656 | {
657 | fprintf(stderr, _("Error: Memory allocation failed\n"));
658 | exit(EXIT_FAILURE);
659 | }
660 |
661 | nco_crcf_set_phase(oscillator, 0);
662 | nco_crcf_set_frequency(oscillator, TAU * ((float) transfer->frequency_offset /
663 | transfer->sample_rate));
664 |
665 | frame_synchronizer = ofdmflexframesync_create(transfer->subcarriers,
666 | transfer->cyclic_prefix_length,
667 | transfer->taper_length,
668 | NULL,
669 | frame_received,
670 | transfer);
671 | frame_properties.check = transfer->crc;
672 | frame_properties.fec0 = transfer->inner_fec;
673 | frame_properties.fec1 = transfer->outer_fec;
674 | frame_properties.mod_scheme = transfer->subcarrier_modulation;
675 | ofdmflexframesync_set_header_props(frame_synchronizer, &frame_properties);
676 | ofdmflexframesync_set_header_len(frame_synchronizer, header_size);
677 |
678 | while((!stop) && (!transfer->stop))
679 | {
680 | n = receive_from_radio(transfer, samples, samples_size);
681 | if((n == 0) &&
682 | ((transfer->radio_type == IO) || (transfer->radio_type == FILENAME)))
683 | {
684 | break;
685 | }
686 | if((transfer->timeout > 0) &&
687 | (time(NULL) > transfer->timeout_start + transfer->timeout))
688 | {
689 | if(verbose)
690 | {
691 | fprintf(stderr, _("Timeout: %d s without frames\n"), transfer->timeout);
692 | }
693 | break;
694 | }
695 | if(transfer->dump)
696 | {
697 | dump_samples(transfer, samples, n);
698 | }
699 | if(transfer->frequency_offset != 0)
700 | {
701 | nco_crcf_mix_block_down(oscillator, samples, samples, n);
702 | }
703 | msresamp_crcf_execute(resampler, samples, n, frame_samples, &n);
704 | ofdmflexframesync_execute(frame_synchronizer, frame_samples, n);
705 | }
706 |
707 | for(n = 0; n < delay; n++)
708 | {
709 | samples[n] = 0;
710 | }
711 | msresamp_crcf_execute(resampler, samples, delay, frame_samples, &n);
712 | ofdmflexframesync_execute(frame_synchronizer, frame_samples, n);
713 | while(ofdmflexframesync_is_frame_open(frame_synchronizer))
714 | {
715 | ofdmflexframesync_execute(frame_synchronizer, samples, 1);
716 | }
717 |
718 | free(samples);
719 | free(frame_samples);
720 | nco_crcf_destroy(oscillator);
721 | msresamp_crcf_destroy(resampler);
722 | ofdmflexframesync_destroy(frame_synchronizer);
723 | }
724 |
725 | ofdm_transfer_t ofdm_transfer_create_callback(char *radio_driver,
726 | unsigned char emit,
727 | int (*data_callback)(void *,
728 | unsigned char *,
729 | unsigned int),
730 | void *callback_context,
731 | unsigned long int sample_rate,
732 | unsigned int bit_rate,
733 | unsigned long int frequency,
734 | long int frequency_offset,
735 | char *gain,
736 | float ppm,
737 | char *subcarrier_modulation,
738 | unsigned int subcarriers,
739 | unsigned int cyclic_prefix_length,
740 | unsigned int taper_length,
741 | char *inner_fec,
742 | char *outer_fec,
743 | char *id,
744 | char *dump,
745 | unsigned int timeout,
746 | unsigned char audio)
747 | {
748 | int direction;
749 | SoapySDRKwargs kwargs;
750 | unsigned int n;
751 | char *gain_name;
752 | int gain_value;
753 | ofdm_transfer_t transfer = malloc(sizeof(struct ofdm_transfer_s));
754 |
755 | if(transfer == NULL)
756 | {
757 | fprintf(stderr, _("Error: Memory allocation failed\n"));
758 | return(NULL);
759 | }
760 | bzero(transfer, sizeof(struct ofdm_transfer_s));
761 |
762 | if(strcasecmp(radio_driver, "io") == 0)
763 | {
764 | transfer->radio_type = IO;
765 | }
766 | else if(strncasecmp(radio_driver, "file=", 5) == 0)
767 | {
768 | transfer->radio_type = FILENAME;
769 | }
770 | else
771 | {
772 | transfer->radio_type = SOAPYSDR;
773 | }
774 |
775 | transfer->stop = 0;
776 | transfer->emit = emit;
777 | transfer->file = NULL;
778 | transfer->data_callback = data_callback;
779 | transfer->callback_context = callback_context;
780 |
781 | if(sample_rate != 0)
782 | {
783 | transfer->sample_rate = sample_rate * ((1000000.0 - ppm) / 1000000.0);
784 | }
785 | else
786 | {
787 | fprintf(stderr, _("Error: Invalid sample rate\n"));
788 | free(transfer);
789 | return(NULL);
790 | }
791 |
792 | if(frequency != 0)
793 | {
794 | transfer->frequency = frequency * ((1000000.0 - ppm) / 1000000.0);
795 | }
796 | else
797 | {
798 | fprintf(stderr, _("Error: Invalid frequency\n"));
799 | free(transfer);
800 | return(NULL);
801 | }
802 |
803 | transfer->frequency_offset = frequency_offset;
804 |
805 | if(audio)
806 | {
807 | if((transfer->radio_type == IO) || (transfer->radio_type == FILENAME))
808 | {
809 | transfer->audio_converter = firhilbf_create(25, 60);
810 | /* The rate of audio samples is twice the rate of IQ samples */
811 | transfer->sample_rate = transfer->sample_rate / 2;
812 | /* -(sample_rate / 2) Hz IQ <=> 0 Hz audio
813 | * (sample_rate / 2) Hz IQ <=> (sample_rate * 2) Hz audio */
814 | transfer->frequency_offset = transfer->frequency - (transfer->sample_rate / 2);
815 | transfer->frequency = 0;
816 | gain_value = strtol(gain, NULL, 10);
817 | transfer->audio_gain = powf(10, gain_value / 20.0);
818 | }
819 | else
820 | {
821 | fprintf(stderr, _("Error: This radio type only supports IQ samples\n"));
822 | free(transfer);
823 | return(NULL);
824 | }
825 | }
826 | else
827 | {
828 | transfer->audio_converter = NULL;
829 | transfer->audio_gain = 0;
830 | }
831 |
832 | if(bit_rate != 0)
833 | {
834 | transfer->bit_rate = bit_rate;
835 | }
836 | else
837 | {
838 | fprintf(stderr, _("Error: Invalid bit rate\n"));
839 | free(transfer);
840 | return(NULL);
841 | }
842 |
843 | transfer->subcarrier_modulation = liquid_getopt_str2mod(subcarrier_modulation);
844 | switch(transfer->subcarrier_modulation)
845 | {
846 | case LIQUID_MODEM_BPSK:
847 | case LIQUID_MODEM_QPSK:
848 | case LIQUID_MODEM_PSK8:
849 | case LIQUID_MODEM_APSK16:
850 | case LIQUID_MODEM_APSK32:
851 | case LIQUID_MODEM_APSK64:
852 | case LIQUID_MODEM_APSK128:
853 | case LIQUID_MODEM_APSK256:
854 | break;
855 |
856 | default:
857 | fprintf(stderr, _("Error: Invalid subcarrier modulation\n"));
858 | free(transfer);
859 | return(NULL);
860 | }
861 |
862 | if(subcarriers != 0)
863 | {
864 | transfer->subcarriers = subcarriers;
865 | }
866 | else
867 | {
868 | fprintf(stderr, _("Error: Invalid number of subcarriers\n"));
869 | free(transfer);
870 | return(NULL);
871 | }
872 |
873 | transfer->cyclic_prefix_length = cyclic_prefix_length;
874 | transfer->taper_length = taper_length;
875 | transfer->crc = LIQUID_CRC_32;
876 |
877 | transfer->inner_fec = liquid_getopt_str2fec(inner_fec);
878 | if(transfer->inner_fec == LIQUID_FEC_UNKNOWN)
879 | {
880 | fprintf(stderr, _("Error: Invalid inner FEC\n"));
881 | free(transfer);
882 | return(NULL);
883 | }
884 |
885 | transfer->outer_fec = liquid_getopt_str2fec(outer_fec);
886 | if(transfer->outer_fec == LIQUID_FEC_UNKNOWN)
887 | {
888 | fprintf(stderr, _("Error: Invalid outer FEC\n"));
889 | free(transfer);
890 | return(NULL);
891 | }
892 |
893 | if(strlen(id) <= 4)
894 | {
895 | strcpy(transfer->id, id);
896 | }
897 | else
898 | {
899 | fprintf(stderr, _("Error: Id must be at most 4 bytes long\n"));
900 | free(transfer);
901 | return(NULL);
902 | }
903 |
904 | if(dump)
905 | {
906 | transfer->dump = fopen(dump, "wb");
907 | if(transfer->dump == NULL)
908 | {
909 | fprintf(stderr, _("Error: Failed to open '%s'\n"), dump);
910 | free(transfer);
911 | return(NULL);
912 | }
913 | }
914 | else
915 | {
916 | transfer->dump = NULL;
917 | }
918 |
919 | transfer->timeout = timeout;
920 |
921 | switch(transfer->radio_type)
922 | {
923 | case IO:
924 | break;
925 |
926 | case FILENAME:
927 | if(emit)
928 | {
929 | transfer->radio_device.file = fopen(radio_driver + 5, "wb");
930 | }
931 | else
932 | {
933 | transfer->radio_device.file = fopen(radio_driver + 5, "rb");
934 | }
935 | if(transfer->radio_device.file == NULL)
936 | {
937 | fprintf(stderr, _("Error: Failed to open '%s'\n"), radio_driver + 5);
938 | free(transfer);
939 | return(NULL);
940 | }
941 | break;
942 |
943 | case SOAPYSDR:
944 | transfer->radio_device.soapysdr = SoapySDRDevice_makeStrArgs(radio_driver);
945 | if(transfer->radio_device.soapysdr == NULL)
946 | {
947 | fprintf(stderr, _("Error: %s\n"), SoapySDRDevice_lastError());
948 | free(transfer);
949 | return(NULL);
950 | }
951 | direction = emit ? SOAPY_SDR_TX : SOAPY_SDR_RX;
952 | SOAPYSDR_CHECK(SoapySDRDevice_setSampleRate(transfer->radio_device.soapysdr,
953 | direction,
954 | 0,
955 | transfer->sample_rate));
956 | SOAPYSDR_CHECK(SoapySDRDevice_setFrequency(transfer->radio_device.soapysdr,
957 | direction,
958 | 0,
959 | transfer->frequency - transfer->frequency_offset,
960 | NULL));
961 | if(strchr(gain, '='))
962 | {
963 | kwargs = SoapySDRKwargs_fromString(gain);
964 | for(n = 0; n < kwargs.size; n++)
965 | {
966 | gain_name = kwargs.keys[n];
967 | gain_value = strtoul(kwargs.vals[n], NULL, 10);
968 | SOAPYSDR_CHECK(SoapySDRDevice_setGainElement(transfer->radio_device.soapysdr,
969 | direction,
970 | 0,
971 | gain_name,
972 | gain_value));
973 | }
974 | SoapySDRKwargs_clear(&kwargs);
975 | }
976 | else
977 | {
978 | gain_value = strtoul(gain, NULL, 10);
979 | SOAPYSDR_CHECK(SoapySDRDevice_setGain(transfer->radio_device.soapysdr,
980 | direction,
981 | 0,
982 | gain_value));
983 | }
984 | transfer->radio_stream.soapysdr = SoapySDRDevice_setupStream(transfer->radio_device.soapysdr,
985 | direction,
986 | SOAPY_SDR_CF32,
987 | NULL,
988 | 0,
989 | NULL);
990 | if(transfer->radio_stream.soapysdr == NULL)
991 | {
992 | fprintf(stderr, _("Error: %s\n"), SoapySDRDevice_lastError());
993 | SoapySDRDevice_unmake(transfer->radio_device.soapysdr);
994 | free(transfer);
995 | return(NULL);
996 | }
997 | break;
998 |
999 | default:
1000 | fprintf(stderr, _("Error: Unknown radio type\n"));
1001 | free(transfer);
1002 | return(NULL);
1003 | break;
1004 | }
1005 |
1006 | return(transfer);
1007 | }
1008 |
1009 | ofdm_transfer_t ofdm_transfer_create(char *radio_driver,
1010 | unsigned char emit,
1011 | char *file,
1012 | unsigned long int sample_rate,
1013 | unsigned int bit_rate,
1014 | unsigned long int frequency,
1015 | long int frequency_offset,
1016 | char *gain,
1017 | float ppm,
1018 | char *subcarrier_modulation,
1019 | unsigned int subcarriers,
1020 | unsigned int cyclic_prefix_length,
1021 | unsigned int taper_length,
1022 | char *inner_fec,
1023 | char *outer_fec,
1024 | char *id,
1025 | char *dump,
1026 | unsigned int timeout,
1027 | unsigned char audio)
1028 | {
1029 | int flags;
1030 | ofdm_transfer_t transfer;
1031 |
1032 | transfer = ofdm_transfer_create_callback(radio_driver,
1033 | emit,
1034 | emit ? read_data : write_data,
1035 | NULL,
1036 | sample_rate,
1037 | bit_rate,
1038 | frequency,
1039 | frequency_offset,
1040 | gain,
1041 | ppm,
1042 | subcarrier_modulation,
1043 | subcarriers,
1044 | cyclic_prefix_length,
1045 | taper_length,
1046 | inner_fec,
1047 | outer_fec,
1048 | id,
1049 | dump,
1050 | timeout,
1051 | audio);
1052 | if(transfer == NULL)
1053 | {
1054 | return(NULL);
1055 | }
1056 |
1057 | transfer->callback_context = transfer;
1058 | if(file)
1059 | {
1060 | if(emit)
1061 | {
1062 | transfer->file = fopen(file, "rb");
1063 | }
1064 | else
1065 | {
1066 | transfer->file = fopen(file, "wb");
1067 | }
1068 | if(transfer->file == NULL)
1069 | {
1070 | fprintf(stderr, _("Error: Failed to open '%s'\n"), file);
1071 | free(transfer);
1072 | return(NULL);
1073 | }
1074 | }
1075 | else
1076 | {
1077 | if(emit)
1078 | {
1079 | transfer->file = stdin;
1080 | flags = fcntl(STDIN_FILENO, F_GETFL);
1081 | fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
1082 | }
1083 | else
1084 | {
1085 | transfer->file = stdout;
1086 | }
1087 | }
1088 |
1089 | return(transfer);
1090 | }
1091 |
1092 | void ofdm_transfer_free(ofdm_transfer_t transfer)
1093 | {
1094 | if(transfer)
1095 | {
1096 | if(transfer->file)
1097 | {
1098 | fclose(transfer->file);
1099 | }
1100 | if(transfer->dump)
1101 | {
1102 | fclose(transfer->dump);
1103 | }
1104 | if(transfer->audio_converter)
1105 | {
1106 | firhilbf_destroy(transfer->audio_converter);
1107 | }
1108 | switch(transfer->radio_type)
1109 | {
1110 | case IO:
1111 | break;
1112 |
1113 | case FILENAME:
1114 | fclose(transfer->radio_device.file);
1115 | break;
1116 |
1117 | case SOAPYSDR:
1118 | SoapySDRDevice_deactivateStream(transfer->radio_device.soapysdr,
1119 | transfer->radio_stream.soapysdr,
1120 | 0,
1121 | 0);
1122 | SoapySDRDevice_closeStream(transfer->radio_device.soapysdr,
1123 | transfer->radio_stream.soapysdr);
1124 | SoapySDRDevice_unmake(transfer->radio_device.soapysdr);
1125 | break;
1126 |
1127 | default:
1128 | break;
1129 | }
1130 | free(transfer);
1131 | }
1132 | }
1133 |
1134 | void ofdm_transfer_start(ofdm_transfer_t transfer)
1135 | {
1136 | stop = 0;
1137 | transfer->stop = 0;
1138 |
1139 | switch(transfer->radio_type)
1140 | {
1141 | case IO:
1142 | if(verbose)
1143 | {
1144 | fprintf(stderr, _("Info: Using IO pseudo-radio\n"));
1145 | }
1146 | break;
1147 |
1148 | case FILENAME:
1149 | if(verbose)
1150 | {
1151 | fprintf(stderr, _("Info: Using FILENAME pseudo-radio\n"));
1152 | }
1153 | break;
1154 |
1155 | case SOAPYSDR:
1156 | SoapySDRDevice_activateStream(transfer->radio_device.soapysdr,
1157 | transfer->radio_stream.soapysdr,
1158 | 0,
1159 | 0,
1160 | 0);
1161 | break;
1162 |
1163 | default:
1164 | return;
1165 | }
1166 |
1167 | transfer->timeout_start = time(NULL);
1168 | if(transfer->emit)
1169 | {
1170 | send_frames(transfer);
1171 | }
1172 | else
1173 | {
1174 | receive_frames(transfer);
1175 | }
1176 | }
1177 |
1178 | void ofdm_transfer_stop(ofdm_transfer_t transfer)
1179 | {
1180 | transfer->stop = 1;
1181 | }
1182 |
1183 | void ofdm_transfer_stop_all()
1184 | {
1185 | stop = 1;
1186 | }
1187 |
1188 | void ofdm_transfer_print_available_radios()
1189 | {
1190 | size_t size;
1191 | unsigned int i;
1192 | unsigned int j;
1193 | char *driver;
1194 | char *serial;
1195 | SoapySDRKwargs *devices = SoapySDRDevice_enumerate(NULL, &size);
1196 |
1197 | if(size == 0)
1198 | {
1199 | printf(_(" No radio detected\n"));
1200 | }
1201 | else
1202 | {
1203 | for(i = 0; i < size; i++)
1204 | {
1205 | driver = NULL;
1206 | serial = NULL;
1207 | for(j = 0; j < devices[i].size; j++)
1208 | {
1209 | if(strcasecmp(devices[i].keys[j], "driver") == 0)
1210 | {
1211 | driver = devices[i].vals[j];
1212 | }
1213 | else if(strcasecmp(devices[i].keys[j], "serial") == 0)
1214 | {
1215 | serial = devices[i].vals[j];
1216 | if(strlen(serial) > 8)
1217 | {
1218 | serial = &serial[strlen(serial) - 8];
1219 | }
1220 | }
1221 | }
1222 | printf(" - driver=%s,serial=%s\n", driver, serial);
1223 | }
1224 | }
1225 | SoapySDRKwargsList_clear(devices, size);
1226 | }
1227 |
1228 | void ofdm_transfer_print_available_subcarrier_modulations()
1229 | {
1230 | printf(" - bpsk\n");
1231 | printf(" - qpsk\n");
1232 | printf(" - psk8\n");
1233 | printf(" - apsk16\n");
1234 | printf(" - apsk32\n");
1235 | printf(" - apsk64\n");
1236 | printf(" - apsk128\n");
1237 | printf(" - apsk256\n");
1238 | }
1239 |
1240 | void ofdm_transfer_print_available_forward_error_codes()
1241 | {
1242 | liquid_print_fec_schemes();
1243 | }
1244 |
--------------------------------------------------------------------------------