This commit is contained in:
gym 2022-01-12 01:39:33 -01:00
commit 1820908eae
17 changed files with 1886 additions and 486 deletions

8
.gitignore vendored
View File

@ -38,6 +38,7 @@ gps-sdr-sim-lut
# Temporary files
*.swp
*~
# Byte-compiled / optimized / DLL files
__pycache__/
@ -177,3 +178,10 @@ dmypy.json
# Cython debug symbols
cython_debug/
# Netbeans project folder
nbproject/*
# Last used USER_MOTION_SIZE variable
.user-motion-size

View File

@ -5,16 +5,40 @@ all: gps-sdr-sim
SHELL=/bin/bash
CC=gcc
CFLAGS=-O3 -Wall
LDFLAGS=-lm -lpthread
CFLAGS=-O3 -Wall -D_FILE_OFFSET_BITS=64
ifdef USER_MOTION_SIZE
CFLAGS+=-DUSER_MOTION_SIZE=$(USER_MOTION_SIZE)
endif
gps-sdr-sim: gpssim.o
${CC} $< ${LDFLAGS} -o $@
gpssim.o: .user-motion-size gpssim.h
.user-motion-size: .FORCE
@if [ -f .user-motion-size ]; then \
if [ "`cat .user-motion-size`" != "$(USER_MOTION_SIZE)" ]; then \
echo "Updating .user-motion-size"; \
echo "$(USER_MOTION_SIZE)" >| .user-motion-size; \
fi; \
else \
echo "$(USER_MOTION_SIZE)" > .user-motion-size; \
fi;
clean:
rm -f gpssim.o gps-sdr-sim *.bin
rm -f gpssim.o gps-sdr-sim *.bin .user-motion-size
time: gps-sdr-sim
time ./gps-sdr-sim -e brdc3540.14n -u circle.csv -b 1
time ./gps-sdr-sim -e brdc3540.14n -u circle.csv -b 8
time ./gps-sdr-sim -e brdc3540.14n -u circle.csv -b 16
.FORCE:
YEAR?=$(shell date +"%Y")
Y=$(patsubst 20%,%,$(YEAR))
%.$(Y)n:
wget -q ftp://cddis.gsfc.nasa.gov/gnss/data/daily/$(YEAR)/brdc/$@.Z -O $@.Z
uncompress $@.Z

View File

@ -2,7 +2,7 @@
GPS-SDR-SIM generates GPS baseband signal data streams, which can be converted
to RF using software-defined radio (SDR) platforms, such as
[bladeRF](http://nuand.com/), [HackRF](https://github.com/mossmann/hackrf/wiki), and [USRP](http://www.ettus.com/).
[ADALM-Pluto](https://wiki.analog.com/university/tools/pluto), [bladeRF](http://nuand.com/), [HackRF](https://github.com/mossmann/hackrf/wiki), and [USRP](http://www.ettus.com/).
### Windows build instructions
@ -18,6 +18,7 @@ to RF using software-defined radio (SDR) platforms, such as
$ gcc gpssim.c -lm -lpthread -O3 -o gps-sdr-sim
```
### Realtime by Gnuradio
Use -n option can connect to a TCP source in Gnuradio for realtime simulation.
@ -36,11 +37,7 @@ cd into /mapserver, and run
python mapper.py
```
Then visit http://127.0.0.1:8080/static/baidumap.html to use the baidu Online map.
Actually I don't want to use BaiduMap.. But because of the GFW, I can't access Google..
Then visit http://127.0.0.1:8080/static/baidumap.html to use the Online map.
You can write an map which can POST data to http://127.0.0.1:8080/post like this
@ -48,7 +45,24 @@ You can write an map which can POST data to http://127.0.0.1:8080/post like this
lon=116&lat=39&hgt=10
```
to replace Baidumap.
### Using bigger user motion files
In order to use user motion files with more than 30000 samples (at 10Hz), the `USER_MOTION_SIZE`
variable can be set to the maximum time of the user motion file in seconds. It is advisable to do
this using make so gps-sdr-bin can update the size when needed. e.g:
```
$ make USER_MOTION_SIZE=4000
```
This variable can also be set when compiling directly with GCC:
```
$ gcc gpssim.c -lm -O3 -o gps-sdr-sim -DUSER_MOTION_SIZE=4000
```
### Generating the GPS signal file
@ -58,20 +72,20 @@ The sampling rate of the user motion has to be 10Hz.
The user is also able to assign a static location directly through the command line.
The user specifies the GPS satellite constellation through a GPS broadcast
ephemeris file. The daily GPS broadcast ephemers file (brdc) is a merge of the
individual site navigation files into one. The archive for the daily file is:
[ftp://cddis.gsfc.nasa.gov/gnss/data/daily/](ftp://cddis.gsfc.nasa.gov/gnss/data/daily/)
ephemeris file. The daily GPS broadcast ephemeris file (brdc) is a merge of the
individual site navigation files into one. The archive for the daily file can
be downloaded from: https://cddis.nasa.gov/archive/gnss/data/daily/. Access
to this site requires registration, which is free.
These files are then used to generate the simulated pseudorange and
Doppler for the GPS satellites in view. This simulated range data is
then used to generate the digitized I/Q samples for the GPS signal.
The bladeRF command line interface requires I/Q pairs stored as signed
The bladeRF and ADALM-Pluto command line interface requires I/Q pairs stored as signed
16-bit integers, while the hackrf_transfer and gps-sdr-sim-uhd.py
support signed bytes.
HackRF and bladeRF require 2.6 MHz sample rate, while the USRP2 requires
HackRF, bladeRF and ADALM-Pluto require 2.6 MHz sample rate, while the USRP2 requires
2.5 MHz (an even integral decimator of 100 MHz).
The simulation start time can be specified if the corresponding set of ephemerides
@ -92,11 +106,12 @@ Options:
-e <gps_nav> RINEX navigation file for GPS ephemerides (required)
-u <user_motion> User motion file (dynamic mode)
-g <nmea_gga> NMEA GGA stream (dynamic mode)
-c <location> ECEF X,Y,Z in meters (static mode) e.g. 3967283.15,1022538.18,4872414.48
-l <location> Lat,Lon,Hgt (static mode) e.g. 30.286502,120.032669,100
-t <date,time> Scenario start time YYYY/MM/DD,hh:mm:ss
-T <date,time> Overwrite TOC and TOE to scenario start time
-d <duration> Duration [sec] (dynamic mode max: 300 static mode max: 86400)
-o <output> I/Q sampling data file (default: gpssim.bin)
-o <output> I/Q sampling data file (default: gpssim.bin ; use - for stdout)
-s <frequency> Sampling frequency [Hz] (default: 2600000)
-b <iq_bits> I/Q data format [1/8/16] (default: 16)
-i Disable ionospheric delay for spacecraft scenario
@ -130,6 +145,8 @@ Use Gnuradio to realtime simulate 3000s:
The TX port of a particular SDR platform is connected to the GPS receiver
under test through a DC block and a fixed 50-60dB attenuator.
#### BladeRF:
The simulated GPS signal file, named "gpssim.bin", can be loaded
into the bladeRF for playback as shown below:
@ -145,25 +162,50 @@ tx start
```
You can also execute these commands via the `bladeRF-cli` script option as below:
```
> bladeRF-cli -s bladerf.script
```
For the HackRF:
#### HackRF:
```
> hackrf_transfer -t gpssim.bin -f 1575420000 -s 2600000 -a 1 -x 0
```
For UHD supported devices (tested with USRP2 only):
#### UHD supported devices (tested with USRP2 only):
```
> gps-sdr-sim-uhd.py -t gpssim.bin -s 2500000 -x 0
```
#### LimeSDR (in case of 1 Msps 1-bit file, to get full BaseBand dynamic and low RF power):
```
> limeplayer -s 1000000 -b 1 -d 2047 -g 0.1 < ../circle.1b.1M.bin
```
#### ADALM-Pluto (PlutoSDR):
The ADALM-Pluto device is expected to have its network interface up and running and is accessible
via "pluto.local" by default.
Default settings:
```
> plutoplayer -t gpssim.bin
```
Set TX attenuation:
```
> plutoplayer -t gpssim.bin -a -30.0
```
Default -20.0dB. Applicable range 0.0dB to -80.0dB in 0.25dB steps.
Set RF bandwidth:
```
> plutoplayer -t gpssim.bin -b 3.0
```
Default 3.0MHz. Applicable range 1.0MHz to 5.0MHz.
### License
Copyright &copy; 2015 Takuji Ebinuma
Copyright &copy; 2015-2018 Takuji Ebinuma
Distributed under the [MIT License](http://www.opensource.org/licenses/mit-license.php).

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# a small script to transmit simulated GPS samples via UHD
# (C) 2015 by Harald Welte <laforge@gnumonks.org>
# Licensed under the MIT License (see LICENSE)
@ -20,16 +20,11 @@ class top_block(gr.top_block):
##################################################
# Blocks
##################################################
self.uhd_usrp_sink = uhd.usrp_sink(
",".join(("", "")),
uhd.stream_args(
cpu_format="fc32",
channels=range(1),
),
)
self.uhd_usrp_sink = uhd.usrp_sink(options.args, uhd.stream_args(cpu_format="fc32"))
self.uhd_usrp_sink.set_samp_rate(options.sample_rate)
self.uhd_usrp_sink.set_center_freq(options.frequency, 0)
self.uhd_usrp_sink.set_gain(options.gain, 0)
self.uhd_usrp_sink.set_clock_source(options.clock_source)
if options.bits == 16:
# a file source for the file generated by the gps-sdr-sim
@ -55,7 +50,13 @@ class top_block(gr.top_block):
self.connect((self.blocks_file_source, 0), (self.blocks_char_to_short, 0))
self.connect((self.blocks_char_to_short, 0), (self.blocks_interleaved_short_to_complex, 0))
self.connect((self.blocks_interleaved_short_to_complex, 0), (self.uhd_usrp_sink, 0))
# scale complex values to within +/- 1.0 so don't overflow USRP DAC
# scale of 1.0/2**11 will scale it to +/- 0.5
self.blocks_multiply_const = blocks.multiply_const_vcc((1.0/2**11, ))
# establish the connections
self.connect((self.blocks_interleaved_short_to_complex, 0), (self.blocks_multiply_const, 0))
self.connect((self.blocks_multiply_const, 0), (self.uhd_usrp_sink, 0))
def get_options():
parser = OptionParser(option_class=eng_option)
@ -71,11 +72,16 @@ def get_options():
help="set output file name [default=gpssim.bin]")
parser.add_option("-b", "--bits", type="eng_float", default=16,
help="set size of every sample [default=16]")
parser.add_option("-a", "--args", type="string", default="",
help="set UHD arguments [default='']")
parser.add_option("-c", "--clock_source", type="string", default="internal",
help="set clock source [default='internal']")
(options, args) = parser.parse_args()
if len(args) != 0:
parser.print_help()
raise SystemExit, 1
raise SystemExit(1)
return (options)
@ -83,6 +89,6 @@ if __name__ == '__main__':
(options) = get_options()
tb = top_block(options)
tb.start()
raw_input('Press Enter to quit: ')
input('Press Enter to quit: ')
tb.stop()
tb.wait()

143
gpssim.c
View File

@ -96,8 +96,8 @@ int allocatedSat[MAX_SAT];
/*! \brief Subtract two vectors of double
* \param[out] y Result of subtraction
* \param[in] x1 Minuend of subtracion
* \param[in] x2 Subtrahend of subtracion
* \param[in] x1 Minuend of subtraction
* \param[in] x2 Subtrahend of subtraction
*/
void subVect(double *y, const double *x1, const double *x2)
{
@ -128,7 +128,7 @@ double dotProd(const double *x1, const double *x2)
}
/* !\brief generate the C/A code sequence for a given Satellite Vehicle PRN
* \param[in] prn PRN nuber of the Satellite Vehicle
* \param[in] prn PRN number of the Satellite Vehicle
* \param[out] ca Caller-allocated integer array of 1023 bytes
*/
void codegen(int *ca, int prn)
@ -220,7 +220,7 @@ void gps2date(const gpstime_t *g, datetime_t *t)
return;
}
/*! \brief Convert Earth-centered Earth-fixed (ECEF) into Lat/Long/Heighth
/*! \brief Convert Earth-centered Earth-fixed (ECEF) into Lat/Long/Height
* \param[in] xyz Input Array of X, Y and Z ECEF coordinates
* \param[out] llh Output Array of Latitude, Longitude and Height
*/
@ -353,7 +353,7 @@ void ecef2neu(const double *xyz, double t[3][3], double *neu)
return;
}
/*! \brief Convert North-Eeast-Up to Azimuth + Elevation
/*! \brief Convert North-East-Up to Azimuth + Elevation
* \param[in] neu Input position in North-East-Up format
* \param[out] azel Output array of azimuth + elevation as double
*/
@ -375,7 +375,7 @@ void neu2azel(double *azel, const double *neu)
* \param[in] eph Ephemeris data of the satellite
* \param[in] g GPS time at which position is to be computed
* \param[out] pos Computed position (vector)
* \param[out] vel Computed velociy (vector)
* \param[out] vel Computed velocity (vector)
* \param[clk] clk Computed clock
*/
void satpos(ephem_t eph, gpstime_t g, double *pos, double *vel, double *clk)
@ -667,7 +667,7 @@ void eph2sbf(const ephem_t eph, const ionoutc_t ionoutc, unsigned long sbf[5][N_
}
/*! \brief Count number of bits set to 1
* \param[in] v long word in whihc bits are counted
* \param[in] v long word in which bits are counted
* \returns Count of bits set to 1
*/
unsigned long countBits(unsigned long v)
@ -730,7 +730,7 @@ unsigned long computeChecksum(unsigned long source, int nib)
if (nib) // Non-information bearing bits for word 2 and 10
{
/*
Solve bits 23 and 24 to presearve parity check
Solve bits 23 and 24 to preserve parity check
with zeros in bits 29 and 30.
*/
@ -812,7 +812,7 @@ gpstime_t incGpsTime(gpstime_t g0, double dt)
return(g1);
}
/*! \brief Read Ephemersi data from the RINEX Navigation file */
/*! \brief Read Ephemeris data from the RINEX Navigation file */
/* \param[out] eph Array of Output SV ephemeris data
* \param[in] fname File name of the RINEX file
* \returns Number of sets of ephemerides in the file
@ -1514,7 +1514,7 @@ int generateNavMsg(gpstime_t g, channel_t *chan, int init)
// Sanity check
if (((chan->dwrd[1])&(0x1FFFFUL<<13)) != ((tow&0x1FFFFUL)<<13))
{
printf("\nWARNING: Invalid TOW in subframe 5.\n");
fprintf(stderr, "\nWARNING: Invalid TOW in subframe 5.\n");
return(0);
}
*/
@ -1620,9 +1620,12 @@ int allocateChannel(channel_t *chan, ephem_t *eph, ionoutc_t ionoutc, gpstime_t
r_ref = rho.range;
phase_ini = (2.0*r_ref - r_xyz)/LAMBDA_L1;
#ifdef FLOAT_CARR_PHASE
chan[i].carr_phase = phase_ini - floor(phase_ini);
#else
phase_ini -= floor(phase_ini);
chan[i].carr_phase = (unsigned int)(512 * 65536.0 * phase_ini);
chan[i].carr_phase = (unsigned int)(512.0 * 65536.0 * phase_ini);
#endif
// Done.
break;
}
@ -1648,11 +1651,12 @@ int allocateChannel(channel_t *chan, ephem_t *eph, ionoutc_t ionoutc, gpstime_t
void usage(void)
{
printf("Usage: gps-sdr-sim [options]\n"
fprintf(stderr, "Usage: gps-sdr-sim [options]\n"
"Options:\n"
" -e <gps_nav> RINEX navigation file for GPS ephemerides (required)\n"
" -u <user_motion> User motion file (dynamic mode)\n"
" -g <nmea_gga> NMEA GGA stream (dynamic mode)\n"
" -c <location> ECEF X,Y,Z in meters (static mode) e.g. 3967283.154,1022538.181,4872414.484\n"
" -l <location> Lat,Lon,Hgt (static mode) e.g. 35.681298,139.766247,10.0\n"
" -t <date,time> Scenario start time YYYY/MM/DD,hh:mm:ss\n"
" -T <date,time> Overwrite TOC and TOE to scenario start time\n"
@ -1727,7 +1731,7 @@ int main(int argc, char *argv[])
int iduration;
int verb;
int timeoverwrite = FALSE; // Overwirte the TOC and TOE in the RINEX file
int timeoverwrite = FALSE; // Overwrite the TOC and TOE in the RINEX file
ionoutc_t ionoutc;
@ -1759,7 +1763,7 @@ int main(int argc, char *argv[])
exit(1);
}
while ((result=getopt(argc,argv,"e:u:g:l:o:s:b:T:t:d:ivn:w"))!=-1)
while ((result=getopt(argc,argv,"e:u:g:c:l:o:s:b:T:t:d:ivn:w"))!=-1)
{
switch (result)
{
@ -1774,6 +1778,11 @@ int main(int argc, char *argv[])
strcpy(umfile, optarg);
nmeaGGA = TRUE;
break;
case 'c':
// Static ECEF coordinates input mode
staticLocationMode = TRUE;
sscanf(optarg,"%lf,%lf,%lf",&xyz[0][0],&xyz[0][1],&xyz[0][2]);
break;
case 'l':
// Static geodetic coordinates input mode
// Added by scateu@gmail.com
@ -1781,6 +1790,7 @@ int main(int argc, char *argv[])
sscanf(optarg,"%lf,%lf,%lf",&llh[0],&llh[1],&llh[2]);
llh[0] = llh[0] / R2D; // convert to RAD
llh[1] = llh[1] / R2D; // convert to RAD
llh2xyz(llh,xyz[0]); // Convert llh to xyz
break;
case 'o':
strcpy(outfile, optarg);
@ -1789,7 +1799,7 @@ int main(int argc, char *argv[])
samp_freq = atof(optarg);
if (samp_freq<1.0e6)
{
printf("ERROR: Invalid sampling frequency.\n");
fprintf(stderr, "ERROR: Invalid sampling frequency.\n");
exit(1);
}
break;
@ -1797,7 +1807,7 @@ int main(int argc, char *argv[])
data_format = atoi(optarg);
if (data_format!=SC01 && data_format!=SC08 && data_format!=SC16)
{
printf("ERROR: Invalid I/Q data format.\n");
fprintf(stderr, "ERROR: Invalid I/Q data format.\n");
exit(1);
}
break;
@ -1827,7 +1837,7 @@ int main(int argc, char *argv[])
if (t0.y<=1980 || t0.m<1 || t0.m>12 || t0.d<1 || t0.d>31 ||
t0.hh<0 || t0.hh>23 || t0.mm<0 || t0.mm>59 || t0.sec<0.0 || t0.sec>=60.0)
{
printf("ERROR: Invalid date and time.\n");
fprintf(stderr, "ERROR: Invalid date and time.\n");
exit(1);
}
t0.sec = floor(t0.sec);
@ -1869,7 +1879,7 @@ int main(int argc, char *argv[])
if (navfile[0]==0)
{
printf("ERROR: GPS ephemeris file is not specified.\n");
fprintf(stderr, "ERROR: GPS ephemeris file is not specified.\n");
exit(1);
}
@ -1884,7 +1894,7 @@ int main(int argc, char *argv[])
if (duration<0.0 || (duration>((double)USER_MOTION_SIZE)/10.0 && !staticLocationMode) || (duration>STATIC_MAX_DURATION && staticLocationMode&&usesocket==false))
{
printf("ERROR: Invalid duration.\n");
fprintf(stderr, "ERROR: Invalid duration.\n");
exit(1);
}
iduration = (int)(duration*10.0 + 0.5);
@ -1910,12 +1920,12 @@ int main(int argc, char *argv[])
if (numd==-1)
{
printf("ERROR: Failed to open user motion / NMEA GGA file.\n");
fprintf(stderr, "ERROR: Failed to open user motion / NMEA GGA file.\n");
exit(1);
}
else if (numd==0)
{
printf("ERROR: Failed to read user motion / NMEA GGA data.\n");
fprintf(stderr, "ERROR: Failed to read user motion / NMEA GGA data.\n");
exit(1);
}
@ -1927,14 +1937,13 @@ int main(int argc, char *argv[])
{
// Static geodetic coordinates input mode: "-l"
// Added by scateu@gmail.com
printf("Using static location mode.\n");
llh2xyz(llh,xyz[0]); // Convert llh to xyz
fprintf(stderr, "Using static location mode.\n");
numd = iduration;
}
/*
printf("xyz = %11.1f, %11.1f, %11.1f\n", xyz[0][0], xyz[0][1], xyz[0][2]);
printf("llh = %11.6f, %11.6f, %11.1f\n", llh[0]*R2D, llh[1]*R2D, llh[2]);
fprintf(stderr, "xyz = %11.1f, %11.1f, %11.1f\n", xyz[0][0], xyz[0][1], xyz[0][2]);
fprintf(stderr, "llh = %11.6f, %11.6f, %11.1f\n", llh[0]*R2D, llh[1]*R2D, llh[2]);
*/
////////////////////////////////////////////////////////////
// Read ephemeris
@ -1944,19 +1953,19 @@ int main(int argc, char *argv[])
if (neph==0)
{
printf("ERROR: No ephemeris available.\n");
fprintf(stderr, "ERROR: No ephemeris available.\n");
exit(1);
}
if ((verb==TRUE)&&(ionoutc.vflg==TRUE))
{
printf(" %12.3e %12.3e %12.3e %12.3e\n",
fprintf(stderr, " %12.3e %12.3e %12.3e %12.3e\n",
ionoutc.alpha0, ionoutc.alpha1, ionoutc.alpha2, ionoutc.alpha3);
printf(" %12.3e %12.3e %12.3e %12.3e\n",
fprintf(stderr, " %12.3e %12.3e %12.3e %12.3e\n",
ionoutc.beta0, ionoutc.beta1, ionoutc.beta2, ionoutc.beta3);
printf(" %19.11e %19.11e %9d %9d\n",
fprintf(stderr, " %19.11e %19.11e %9d %9d\n",
ionoutc.A0, ionoutc.A1, ionoutc.tot, ionoutc.wnt);
printf("%6d\n", ionoutc.dtls);
fprintf(stderr, "%6d\n", ionoutc.dtls);
}
for (sv=0; sv<MAX_SAT; sv++)
@ -1969,6 +1978,14 @@ int main(int argc, char *argv[])
}
}
gmax.sec = 0;
gmax.week = 0;
tmax.sec = 0;
tmax.mm = 0;
tmax.hh = 0;
tmax.d = 0;
tmax.m = 0;
tmax.y = 0;
for (sv=0; sv<MAX_SAT; sv++)
{
if (eph[neph-1][sv].vflg == 1)
@ -2021,11 +2038,11 @@ int main(int argc, char *argv[])
{
if (subGpsTime(g0, gmin)<0.0 || subGpsTime(gmax, g0)<0.0)
{
printf("ERROR: Invalid start time.\n");
printf("tmin = %4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)\n",
fprintf(stderr, "ERROR: Invalid start time.\n");
fprintf(stderr, "tmin = %4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)\n",
tmin.y, tmin.m, tmin.d, tmin.hh, tmin.mm, tmin.sec,
gmin.week, gmin.sec);
printf("tmax = %4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)\n",
fprintf(stderr, "tmax = %4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)\n",
tmax.y, tmax.m, tmax.d, tmax.hh, tmax.mm, tmax.sec,
gmax.week, gmax.sec);
exit(1);
@ -2038,9 +2055,9 @@ int main(int argc, char *argv[])
t0 = tmin;
}
printf("Start time = %4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)\n",
fprintf(stderr, "Start time = %4d/%02d/%02d,%02d:%02d:%02.0f (%d:%.0f)\n",
t0.y, t0.m, t0.d, t0.hh, t0.mm, t0.sec, g0.week, g0.sec);
printf("Duration = %.1f [sec]\n", ((double)numd)/10.0);
fprintf(stderr, "Duration = %.1f [sec]\n", ((double)numd)/10.0);
// Select the current set of ephemerides
ieph = -1;
@ -2066,7 +2083,7 @@ int main(int argc, char *argv[])
if (ieph == -1)
{
printf("ERROR: No current set of ephemerides has been found.\n");
fprintf(stderr, "ERROR: No current set of ephemerides has been found.\n");
exit(1);
}
@ -2079,7 +2096,7 @@ int main(int argc, char *argv[])
if (iq_buff==NULL)
{
printf("ERROR: Faild to allocate 16-bit I/Q buffer.\n");
fprintf(stderr, "ERROR: Failed to allocate 16-bit I/Q buffer.\n");
exit(1);
}
@ -2088,7 +2105,7 @@ int main(int argc, char *argv[])
iq8_buff = calloc(2*iq_buff_size, 1);
if (iq8_buff==NULL)
{
printf("ERROR: Faild to allocate 8-bit I/Q buffer.\n");
fprintf(stderr, "ERROR: Failed to allocate 8-bit I/Q buffer.\n");
exit(1);
}
}
@ -2097,17 +2114,22 @@ int main(int argc, char *argv[])
iq8_buff = calloc(iq_buff_size/4, 1); // byte = {I0, Q0, I1, Q1, I2, Q2, I3, Q3}
if (iq8_buff==NULL)
{
printf("ERROR: Faild to allocate compressed 1-bit I/Q buffer.\n");
fprintf(stderr, "ERROR: Failed to allocate compressed 1-bit I/Q buffer.\n");
exit(1);
}
}
if(usesocket==false){
// Open output file
// "-" can be used as name for stdout
if(strcmp("-", outfile)){
if (NULL==(fp=fopen(outfile,"wb")))
{
printf("ERROR: Failed to open output file.\n");
fprintf(stderr, "ERROR: Failed to open output file.\n");
exit(1);
}
}else{
fp = stdout;
}
}
////////////////////////////////////////////////////////////
// Initialize channels
@ -2130,7 +2152,7 @@ int main(int argc, char *argv[])
for(i=0; i<MAX_CHAN; i++)
{
if (chan[i].prn>0)
printf("%02d %6.1f %5.1f %11.1f %5.1f\n", chan[i].prn,
fprintf(stderr, "%02d %6.1f %5.1f %11.1f %5.1f\n", chan[i].prn,
chan[i].azel[0]*R2D, chan[i].azel[1]*R2D, chan[i].rho0.d, chan[i].rho0.iono_delay);
}
@ -2171,8 +2193,9 @@ int main(int argc, char *argv[])
// Update code phase and data bit counters
computeCodePhase(&chan[i], rho, 0.1);
chan[i].carr_phasestep = (int)(512 * 65536.0 * chan[i].f_carr * delt);
#ifndef FLOAT_CARR_PHASE
chan[i].carr_phasestep = (int)round(512.0 * 65536.0 * chan[i].f_carr * delt);
#endif
// Path loss
path_loss = 20200000.0/rho.d;
@ -2194,8 +2217,11 @@ int main(int argc, char *argv[])
{
if (chan[i].prn>0)
{
iTable = (chan[i].carr_phase >> 16) & 511;
#ifdef FLOAT_CARR_PHASE
iTable = (int)floor(chan[i].carr_phase*512.0);
#else
iTable = (chan[i].carr_phase >> 16) & 0x1ff; // 9-bit index
#endif
ip = chan[i].dataBit * chan[i].codeCA * cosTable512[iTable] * gain[i];
qp = chan[i].dataBit * chan[i].codeCA * sinTable512[iTable] * gain[i];
@ -2223,7 +2249,7 @@ int main(int argc, char *argv[])
chan[i].iword++;
/*
if (chan[i].iword>=N_DWRD)
printf("\nWARNING: Subframe word buffer overflow.\n");
fprintf(stderr, "\nWARNING: Subframe word buffer overflow.\n");
*/
}
@ -2232,11 +2258,20 @@ int main(int argc, char *argv[])
}
}
// Set currnt code chip
// Set current code chip
chan[i].codeCA = chan[i].ca[(int)chan[i].code_phase]*2-1;
// Update carrier phase
#ifdef FLOAT_CARR_PHASE
chan[i].carr_phase += chan[i].f_carr * delt;
if (chan[i].carr_phase >= 1.0)
chan[i].carr_phase -= 1.0;
else if (chan[i].carr_phase<0.0)
chan[i].carr_phase += 1.0;
#else
chan[i].carr_phase += chan[i].carr_phasestep;
#endif
}
}
@ -2326,14 +2361,14 @@ int main(int argc, char *argv[])
else
allocateChannel(chan, eph[ieph], ionoutc, grx, xyz[0], elvmask);
// Show ditails about simulated channels
// Show details about simulated channels
if (verb==TRUE)
{
printf("\n");
fprintf(stderr, "\n");
for (i=0; i<MAX_CHAN; i++)
{
if (chan[i].prn>0)
printf("%02d %6.1f %5.1f %11.1f %5.1f\n", chan[i].prn,
fprintf(stderr, "%02d %6.1f %5.1f %11.1f %5.1f\n", chan[i].prn,
chan[i].azel[0]*R2D, chan[i].azel[1]*R2D, chan[i].rho0.d, chan[i].rho0.iono_delay);
}
}
@ -2352,13 +2387,13 @@ int main(int argc, char *argv[])
grx = incGpsTime(grx, 0.1);
// Update time counter
printf("\rTime into run = %4.1f", subGpsTime(grx, g0));
fprintf(stderr, "\rTime into run = %4.1f", subGpsTime(grx, g0));
fflush(stdout);
}
tend = clock();
printf("\nDone!\n");
fprintf(stderr, "\nDone!\n");
// Free I/Q buffer
free(iq_buff);
@ -2368,7 +2403,7 @@ int main(int argc, char *argv[])
fclose(fp);
else sockclose(sockc);
// Process time
printf("Process time = %.1f [sec]\n", (double)(tend-tstart)/CLOCKS_PER_SEC);
fprintf(stderr, "Process time = %.1f [sec]\n", (double)(tend-tstart)/CLOCKS_PER_SEC);
return(0);
}

View File

@ -1,6 +1,8 @@
#ifndef GPSSIM_H
#define GPSSIM_H
#define FLOAT_CARR_PHASE // For RKT simulation. Higher computational load, but smoother carrier phase.
#define TRUE (1)
#define FALSE (0)
#define true (1)
@ -15,7 +17,9 @@
#define MAX_CHAN (16)
/*! \brief Maximum number of user motion points */
#ifndef USER_MOTION_SIZE
#define USER_MOTION_SIZE (3000) // max duration at 10Hz
#endif
/*! \brief Maximum duration for static mode*/
#define STATIC_MAX_DURATION (86400) // second
@ -160,8 +164,12 @@ typedef struct
int ca[CA_SEQ_LEN]; /*< C/A Sequence */
double f_carr; /*< Carrier frequency */
double f_code; /*< Code frequency */
#ifdef FLOAT_CARR_PHASE
double carr_phase;
#else
unsigned int carr_phase; /*< Carrier phase */
int carr_phasestep; /*< Carrier phasestep */
#endif
double code_phase; /*< Code phase */
gpstime_t g0; /*!< GPS time at start */
unsigned long sbf[5][N_DWRD_SBF]; /*!< current subframe */

28
player/Makefile Normal file
View File

@ -0,0 +1,28 @@
DIALECT = -std=c11
CFLAGS += $(DIALECT) -O3 -g -W -Wall
LIBS = -lm
CFLAGS += $(shell pkg-config --cflags libbladeRF)
CFLAGS += $(shell pkg-config --cflags libhackrf)
CFLAGS += $(shell pkg-config --cflags libiio libad9361)
.PHONY: all bladeplayer hackplayer limeplayer plutoplayer clean
all: bladeplayer hackplayer limeplayer plutoplayer
%.o: %.c *.h
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
bladeplayer: bladeplayer.o $(SDR_OBJ) $(COMPAT)
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(shell pkg-config --libs libbladeRF)
hackplayer: hackplayer.o $(COMPAT)
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(shell pkg-config --libs libhackrf)
limeplayer: limeplayer.c
gcc -O2 -Wall -o limeplayer limeplayer.c -lLimeSuite
plutoplayer: plutoplayer.o $(COMPAT)
$(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(shell pkg-config --libs libiio libad9361)
clean:
rm -f *.o bladeplayer hackplayer limeplayer plutoplayer

59
player/README.md Normal file
View File

@ -0,0 +1,59 @@
## How to build the player software on Linux
### bladeRF
#### Build and install libbladeRF
https://github.com/Nuand/bladeRF/wiki/Getting-Started:-Linux
#### Build bladeplayer
```
$ make bladeplayer
```
### HackRF One
#### Build and install libhackrf
https://github.com/mossmann/hackrf/tree/master/host
#### Build hackplayer
```
$ make hackplayer
```
### LimeSDR
#### Build and install libLimeSuite
http://wiki.myriadrf.org/Lime_Suite
#### Build limeplayer
```
$ make limeplayer
```
### ADALM-Pluto
#### Build and install libiio
https://wiki.analog.com/resources/tools-software/linux-software/libiio
#### Build and insatall libad9361
```
$ git clone https://github.com/analogdevicesinc/libad9361-iio.git
$ cd libad9361-iio
$ cmake ./
$ make all
$ sudo make install
```
#### Build plutoplayer
```
$ make plutoplayer
```

View File

@ -7,7 +7,9 @@
#ifdef _WIN32
#include "getopt.h"
#else
#include <getopt.h>
#include <unistd.h>
#include <errno.h>
#endif
#define TX_FREQUENCY 1575420000
@ -25,291 +27,291 @@
void usage(void)
{
fprintf(stderr, "Usage: bladeplayer [options]\n"
" -f <tx_file> I/Q sampling data file (required)\n"
" -b <iq_bits> I/Q data format [1/16] (default: 16)\n"
" -g <tx_vga1> TX VGA1 gain (default: %d)\n",
TX_VGA1);
fprintf(stderr, "Usage: bladeplayer [options]\n"
" -f <tx_file> I/Q sampling data file (required)\n"
" -b <iq_bits> I/Q data format [1/16] (default: 16)\n"
" -g <tx_vga1> TX VGA1 gain (default: %d)\n",
TX_VGA1);
return;
return;
}
int main(int argc, char *argv[])
{
int status;
char *devstr = NULL;
struct bladerf *dev = NULL;
int status;
char *devstr = NULL;
struct bladerf *dev = NULL;
FILE *fp;
int16_t *tx_buffer;
enum state {INIT, READ_FILE, PAD_TRAILING, DONE};
enum state state = INIT;
FILE *fp;
int16_t *tx_buffer;
enum state {INIT, READ_FILE, PAD_TRAILING, DONE};
enum state state = INIT;
int compressed = 0;
uint8_t *read_buffer;
size_t samples_read;
int16_t lut[256][8];
int16_t amp = AMPLITUDE;
int i,k;
int compressed = 0;
uint8_t *read_buffer;
size_t samples_read;
int16_t lut[256][8];
int16_t amp = AMPLITUDE;
uint32_t i,k;
int gain = TX_VGA1;
int result;
int data_format;
char txfile[128];
int gain = TX_VGA1;
int result;
int data_format;
char txfile[128];
// Empty TX file name
txfile[0] = 0;
// Empty TX file name
txfile[0] = 0;
if (argc<3) {
usage();
exit(1);
}
if (argc<3) {
usage();
exit(1);
}
while ((result=getopt(argc,argv,"g:b:f:"))!=-1)
{
switch (result)
{
case 'g':
gain = atoi(optarg);
if (gain>-4 || gain<-35)
{
printf("ERROR: Invalid TX VGA1 gain.\n");
exit(1);
}
break;
case 'b':
data_format = atoi(optarg);
if (data_format!=1 && data_format!=16)
{
printf("ERROR: Invalid I/Q data format.\n");
exit(1);
}
else if (data_format==1)
compressed = 1;
break;
case 'f':
strcpy(txfile, optarg);
break;
case ':':
case '?':
usage();
exit(1);
default:
break;
}
}
while ((result=getopt(argc,argv,"g:b:f:"))!=-1)
{
switch (result)
{
case 'g':
gain = atoi(optarg);
if (gain>-4 || gain<-35)
{
printf("ERROR: Invalid TX VGA1 gain.\n");
exit(1);
}
break;
case 'b':
data_format = atoi(optarg);
if (data_format!=1 && data_format!=16)
{
printf("ERROR: Invalid I/Q data format.\n");
exit(1);
}
else if (data_format==1)
compressed = 1;
break;
case 'f':
strcpy(txfile, optarg);
break;
case ':':
case '?':
usage();
exit(1);
default:
break;
}
}
// Open TX file.
if (txfile[0]==0)
{
printf("ERROR: I/Q sampling data file is not specified.\n");
exit(1);
}
// Open TX file.
if (txfile[0]==0)
{
printf("ERROR: I/Q sampling data file is not specified.\n");
exit(1);
}
fp = fopen(txfile, "rb");
fp = fopen(txfile, "rb");
if (fp==NULL) {
fprintf(stderr, "ERROR: Failed to open TX file: %s\n", argv[1]);
exit(1);
}
if (fp==NULL) {
fprintf(stderr, "ERROR: Failed to open TX file: %s\n", argv[1]);
exit(1);
}
// Initializing device.
printf("Opening and initializing device...\n");
// Initializing device.
printf("Opening and initializing device...\n");
status = bladerf_open(&dev, devstr);
if (status != 0) {
fprintf(stderr, "Failed to open device: %s\n", bladerf_strerror(status));
goto out;
}
status = bladerf_open(&dev, devstr);
if (status != 0) {
fprintf(stderr, "Failed to open device: %s\n", bladerf_strerror(status));
goto out;
}
status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, TX_FREQUENCY);
if (status != 0) {
fprintf(stderr, "Faield to set TX frequency: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX frequency: %u Hz\n", TX_FREQUENCY);
}
status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, TX_FREQUENCY);
if (status != 0) {
fprintf(stderr, "Faield to set TX frequency: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX frequency: %u Hz\n", TX_FREQUENCY);
}
status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, TX_SAMPLERATE, NULL);
if (status != 0) {
fprintf(stderr, "Failed to set TX sample rate: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX sample rate: %u sps\n", TX_SAMPLERATE);
}
status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, TX_SAMPLERATE, NULL);
if (status != 0) {
fprintf(stderr, "Failed to set TX sample rate: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX sample rate: %u sps\n", TX_SAMPLERATE);
}
status = bladerf_set_bandwidth(dev, BLADERF_MODULE_TX, TX_BANDWIDTH, NULL);
if (status != 0) {
fprintf(stderr, "Failed to set TX bandwidth: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX bandwidth: %u Hz\n", TX_BANDWIDTH);
}
status = bladerf_set_bandwidth(dev, BLADERF_MODULE_TX, TX_BANDWIDTH, NULL);
if (status != 0) {
fprintf(stderr, "Failed to set TX bandwidth: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX bandwidth: %u Hz\n", TX_BANDWIDTH);
}
status = bladerf_set_txvga1(dev, gain);
if (status != 0) {
fprintf(stderr, "Failed to set TX VGA1 gain: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX VGA1 gain: %d dB\n", gain);
}
status = bladerf_set_txvga1(dev, gain);
if (status != 0) {
fprintf(stderr, "Failed to set TX VGA1 gain: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX VGA1 gain: %d dB\n", gain);
}
status = bladerf_set_txvga2(dev, TX_VGA2);
if (status != 0) {
fprintf(stderr, "Failed to set TX VGA2 gain: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX VGA2 gain: %d dB\n", TX_VGA2);
}
status = bladerf_set_txvga2(dev, TX_VGA2);
if (status != 0) {
fprintf(stderr, "Failed to set TX VGA2 gain: %s\n", bladerf_strerror(status));
goto out;
}
else {
printf("TX VGA2 gain: %d dB\n", TX_VGA2);
}
// Application code goes here.
printf("Running...\n");
// Application code goes here.
printf("Running...\n");
// Allocate a buffer to hold each block of samples to transmit.
tx_buffer = (int16_t*)malloc(SAMPLES_PER_BUFFER * 2 * sizeof(int16_t));
// Allocate a buffer to hold each block of samples to transmit.
tx_buffer = (int16_t*)malloc(SAMPLES_PER_BUFFER * 2 * sizeof(int16_t));
if (tx_buffer == NULL) {
fprintf(stderr, "Failed to allocate TX buffer.\n");
goto out;
}
if (tx_buffer == NULL) {
fprintf(stderr, "Failed to allocate TX buffer.\n");
goto out;
}
// if compressed
read_buffer = (uint8_t*)malloc(SAMPLES_PER_BUFFER / 4);
// if compressed
read_buffer = (uint8_t*)malloc(SAMPLES_PER_BUFFER / 4);
if (read_buffer == NULL) {
fprintf(stderr, "Failed to allocate read buffer.\n");
goto out;
}
if (read_buffer == NULL) {
fprintf(stderr, "Failed to allocate read buffer.\n");
goto out;
}
for (i=0; i<256; i++)
{
for (k=0; k<8; k++)
lut[i][k] = ((i>>(7-k))&0x1)?amp:-amp;
}
for (i=0; i<256; i++)
{
for (k=0; k<8; k++)
lut[i][k] = ((i>>(7-k))&0x1)?amp:-amp;
}
// Configure the TX module for use with the synchronous interface.
status = bladerf_sync_config(dev,
BLADERF_MODULE_TX,
BLADERF_FORMAT_SC16_Q11,
NUM_BUFFERS,
SAMPLES_PER_BUFFER,
NUM_TRANSFERS,
TIMEOUT_MS);
// Configure the TX module for use with the synchronous interface.
status = bladerf_sync_config(dev,
BLADERF_MODULE_TX,
BLADERF_FORMAT_SC16_Q11,
NUM_BUFFERS,
SAMPLES_PER_BUFFER,
NUM_TRANSFERS,
TIMEOUT_MS);
if (status != 0) {
fprintf(stderr, "Failed to configure TX sync interface: %s\n", bladerf_strerror(status));
goto out;
}
if (status != 0) {
fprintf(stderr, "Failed to configure TX sync interface: %s\n", bladerf_strerror(status));
goto out;
}
// We must always enable the modules *after* calling bladerf_sync_config().
status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true);
if (status != 0) {
fprintf(stderr, "Failed to enable TX module: %s\n", bladerf_strerror(status));
goto out;
}
// We must always enable the modules *after* calling bladerf_sync_config().
status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true);
if (status != 0) {
fprintf(stderr, "Failed to enable TX module: %s\n", bladerf_strerror(status));
goto out;
}
// Keep writing samples while there is more data to send and no failures have occurred.
while (state != DONE && status == 0) {
// Keep writing samples while there is more data to send and no failures have occurred.
while (state != DONE && status == 0) {
int16_t *tx_buffer_current = tx_buffer;
unsigned int buffer_samples_remaining = SAMPLES_PER_BUFFER;
int16_t *tx_buffer_current = tx_buffer;
unsigned int buffer_samples_remaining = SAMPLES_PER_BUFFER;
// if compressed
unsigned int read_samples_remaining = SAMPLES_PER_BUFFER / 4;
// if compressed
unsigned int read_samples_remaining = SAMPLES_PER_BUFFER / 4;
// Keep adding to the buffer until it is full or a failure occurs
while (buffer_samples_remaining > 0 && status == 0 && state != DONE) {
size_t samples_populated = 0;
// Keep adding to the buffer until it is full or a failure occurs
while (buffer_samples_remaining > 0 && status == 0 && state != DONE) {
size_t samples_populated = 0;
switch(state) {
case INIT:
case READ_FILE:
// Read from the input file
if (compressed)
{
int16_t *write_buffer_current = tx_buffer;
switch(state) {
case INIT:
case READ_FILE:
// Read from the input file
if (compressed)
{
int16_t *write_buffer_current = tx_buffer;
samples_read = fread(read_buffer,
sizeof(uint8_t),
read_samples_remaining,
fp);
samples_read = fread(read_buffer,
sizeof(uint8_t),
read_samples_remaining,
fp);
samples_populated = samples_read * 4;
buffer_samples_remaining = read_samples_remaining * 4;
samples_populated = samples_read * 4;
buffer_samples_remaining = read_samples_remaining * 4;
// Expand compressed data into TX buffer
for (i=0; i<samples_read; i++)
{
memcpy(write_buffer_current, lut[read_buffer[i]], 8);
// Expand compressed data into TX buffer
for (i=0; i<samples_read; i++)
{
memcpy(write_buffer_current, lut[read_buffer[i]], 8);
// Advance the write buffer pointer
write_buffer_current += 8;
}
}
else
{
samples_populated = fread(tx_buffer_current,
2 * sizeof(int16_t),
buffer_samples_remaining,
fp);
}
// Advance the write buffer pointer
write_buffer_current += 8;
}
}
else
{
samples_populated = fread(tx_buffer_current,
2 * sizeof(int16_t),
buffer_samples_remaining,
fp);
}
// If the end of the file was reached, pad the rest of the buffer and finish.
if (feof(fp)) {
state = PAD_TRAILING;
}
// Check for errors
else if (ferror(fp)) {
status = errno;
}
// If the end of the file was reached, pad the rest of the buffer and finish.
if (feof(fp)) {
state = PAD_TRAILING;
}
// Check for errors
else if (ferror(fp)) {
status = errno;
}
break;
break;
case PAD_TRAILING:
// Populate the remainder of the buffer with zeros.
memset(tx_buffer_current, 0, buffer_samples_remaining * 2 * sizeof(uint16_t));
case PAD_TRAILING:
// Populate the remainder of the buffer with zeros.
memset(tx_buffer_current, 0, buffer_samples_remaining * 2 * sizeof(uint16_t));
state = DONE;
break;
state = DONE;
break;
case DONE:
default:
break;
}
case DONE:
default:
break;
}
// Advance the buffer pointer.
buffer_samples_remaining -= (unsigned int)samples_populated;
tx_buffer_current += (2 * samples_populated);
}
// Advance the buffer pointer.
buffer_samples_remaining -= (unsigned int)samples_populated;
tx_buffer_current += (2 * samples_populated);
}
// If there were no errors, transmit the data buffer.
if (status == 0) {
bladerf_sync_tx(dev, tx_buffer, SAMPLES_PER_BUFFER, NULL, TIMEOUT_MS);
}
}
// If there were no errors, transmit the data buffer.
if (status == 0) {
bladerf_sync_tx(dev, tx_buffer, SAMPLES_PER_BUFFER, NULL, TIMEOUT_MS);
}
}
// Disable TX module, shutting down our underlying TX stream.
status = bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
if (status != 0) {
fprintf(stderr, "Failed to disable TX module: %s\n", bladerf_strerror(status));
}
// Disable TX module, shutting down our underlying TX stream.
status = bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
if (status != 0) {
fprintf(stderr, "Failed to disable TX module: %s\n", bladerf_strerror(status));
}
// Free up our resources
free(tx_buffer);
// Free up our resources
free(tx_buffer);
// if compressed
free(read_buffer);
// if compressed
free(read_buffer);
// Close TX file
fclose(fp);
// Close TX file
fclose(fp);
out:
printf("Closing device...\n");
bladerf_close(dev);
printf("Closing device...\n");
bladerf_close(dev);
return(0);
return(0);
}

View File

@ -1,22 +1,25 @@
#define _CRT_SECURE_NO_WARNINGS
#include <hackrf.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#ifdef _WIN32
#include <windows.h>
#ifdef _WIN64
typedef int64_t ssize_t;
#else
typedef int32_t ssize_t;
#endif
typedef int bool;
#define true 1
#define false 0
#include "getopt.h"
#else
#include <stdbool.h>
#include <sys/types.h>
#include <getopt.h>
#include <signal.h>
#endif
#include <libhackrf/hackrf.h>
static hackrf_device* device = NULL;
@ -25,199 +28,210 @@ volatile uint32_t byte_count = 0;
volatile bool do_exit = false;
static transceiver_mode_t transceiver_mode = TRANSCEIVER_MODE_TX;
//static transceiver_mode_t transceiver_mode = TRANSCEIVER_MODE_TX;
#define FD_BUFFER_SIZE (8*1024)
#define FREQ_ONE_MHZ (1000000ull)
#ifdef _WIN32
BOOL WINAPI sighandler(int signum)
{
if (CTRL_C_EVENT == signum) {
fprintf(stdout, "Caught signal %d\n", signum);
do_exit = true;
return TRUE;
}
return FALSE;
if(CTRL_C_EVENT == signum) {
fprintf(stdout, "Caught signal %d\n", signum);
do_exit = true;
return TRUE;
}
return FALSE;
}
#else
static void sighandler(int signum) {
fprintf(stdout, "Caught signal %d\n", signum);
do_exit = true;
}
#endif
int tx_callback(hackrf_transfer* transfer) {
size_t bytes_to_read;
size_t bytes_to_read;
if( fd != NULL )
{
ssize_t bytes_read;
byte_count += transfer->valid_length;
bytes_to_read = transfer->valid_length;
if( fd != NULL )
{
ssize_t bytes_read;
byte_count += transfer->valid_length;
bytes_to_read = transfer->valid_length;
bytes_read = fread(transfer->buffer, 1, bytes_to_read, fd);
bytes_read = fread(transfer->buffer, 1, bytes_to_read, fd);
if (bytes_read != bytes_to_read) {
return -1; // EOF
} else {
return 0;
}
} else {
return -1;
}
if (bytes_read != bytes_to_read) {
return -1; // EOF
} else {
return 0;
}
} else {
return -1;
}
}
static void usage() {
fprintf(stderr, "Usage: hackplayer [options]\n"
" -t <filename> Transmit data from file (required)\n");
fprintf(stderr, "Usage: hackplayer [options]\n"
" -t <filename> Transmit data from file (required)\n");
return;
return;
}
int main(int argc, char** argv) {
int opt;
int result;
const char* path = NULL;
uint32_t sample_rate_hz = 2600000;
uint32_t baseband_filter_bw_hz = 0;
unsigned int txvga_gain=0;
uint64_t freq_hz = 1575420000;
uint32_t amp_enable = 1;
int opt;
int result;
const char* path = NULL;
uint32_t sample_rate_hz = 2600000;
uint32_t baseband_filter_bw_hz = 0;
unsigned int txvga_gain=0;
uint64_t freq_hz = 1575420000;
uint32_t amp_enable = 1;
while( (opt = getopt(argc, argv, "t:")) != EOF )
{
result = HACKRF_SUCCESS;
switch( opt )
{
case 't':
path = optarg;
break;
default:
printf("unknown argument '-%c %s'\n", opt, optarg);
usage();
return EXIT_FAILURE;
}
while( (opt = getopt(argc, argv, "t:")) != EOF )
{
result = HACKRF_SUCCESS;
switch( opt )
{
case 't':
path = optarg;
break;
default:
printf("unknown argument '-%c %s'\n", opt, optarg);
usage();
return EXIT_FAILURE;
}
if( result != HACKRF_SUCCESS ) {
printf("argument error: '-%c %s' %s (%d)\n", opt, optarg, hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
}
if( result != HACKRF_SUCCESS ) {
printf("argument error: '-%c %s' %s (%d)\n", opt, optarg, hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
}
if( path == NULL ) {
printf("specify a path to a file to transmit\n");
usage();
return EXIT_FAILURE;
}
if( path == NULL ) {
printf("specify a path to a file to transmit\n");
usage();
return EXIT_FAILURE;
}
// Compute default value depending on sample rate
baseband_filter_bw_hz = hackrf_compute_baseband_filter_bw_round_down_lt(sample_rate_hz);
// Compute default value depending on sample rate
baseband_filter_bw_hz = hackrf_compute_baseband_filter_bw_round_down_lt(sample_rate_hz);
result = hackrf_init();
if( result != HACKRF_SUCCESS ) {
printf("hackrf_init() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
result = hackrf_init();
if( result != HACKRF_SUCCESS ) {
printf("hackrf_init() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
result = hackrf_open_by_serial(NULL, &device);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_open() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
result = hackrf_open_by_serial(NULL, &device);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_open() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
fd = fopen(path, "rb");
if( fd == NULL ) {
printf("Failed to open file: %s\n", path);
return EXIT_FAILURE;
}
fd = fopen(path, "rb");
if( fd == NULL ) {
printf("Failed to open file: %s\n", path);
return EXIT_FAILURE;
}
// Change fd buffer to have bigger one to store or read data on/to HDD
result = setvbuf(fd , NULL , _IOFBF , FD_BUFFER_SIZE);
if( result != 0 ) {
printf("setvbuf() failed: %d\n", result);
usage();
return EXIT_FAILURE;
}
// Change fd buffer to have bigger one to store or read data on/to HDD
result = setvbuf(fd , NULL , _IOFBF , FD_BUFFER_SIZE);
if( result != 0 ) {
printf("setvbuf() failed: %d\n", result);
usage();
return EXIT_FAILURE;
}
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE );
#ifdef _WIN32
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE );
#else
signal(SIGINT, sighandler);
#endif
printf("call hackrf_sample_rate_set(%.03f MHz)\n", ((float)sample_rate_hz/(float)FREQ_ONE_MHZ));
result = hackrf_set_sample_rate_manual(device, sample_rate_hz, 1);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_sample_rate_set() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
printf("call hackrf_sample_rate_set(%.03f MHz)\n", ((float)sample_rate_hz/(float)FREQ_ONE_MHZ));
result = hackrf_set_sample_rate_manual(device, sample_rate_hz, 1);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_sample_rate_set() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
printf("call hackrf_baseband_filter_bandwidth_set(%.03f MHz)\n",
((float)baseband_filter_bw_hz/(float)FREQ_ONE_MHZ));
result = hackrf_set_baseband_filter_bandwidth(device, baseband_filter_bw_hz);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_baseband_filter_bandwidth_set() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
printf("call hackrf_baseband_filter_bandwidth_set(%.03f MHz)\n",
((float)baseband_filter_bw_hz/(float)FREQ_ONE_MHZ));
result = hackrf_set_baseband_filter_bandwidth(device, baseband_filter_bw_hz);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_baseband_filter_bandwidth_set() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
result = hackrf_set_txvga_gain(device, txvga_gain);
result |= hackrf_start_tx(device, tx_callback, NULL);
result = hackrf_set_txvga_gain(device, txvga_gain);
result |= hackrf_start_tx(device, tx_callback, NULL);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_start_?x() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
if( result != HACKRF_SUCCESS ) {
printf("hackrf_start_?x() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
printf("call hackrf_set_freq(%.03f MHz)\n", ((double)freq_hz/(double)FREQ_ONE_MHZ));
result = hackrf_set_freq(device, freq_hz);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_set_freq() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
printf("call hackrf_set_freq(%.03f MHz)\n", ((double)freq_hz/(double)FREQ_ONE_MHZ));
result = hackrf_set_freq(device, freq_hz);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_set_freq() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
printf("call hackrf_set_amp_enable(%u)\n", amp_enable);
result = hackrf_set_amp_enable(device, (uint8_t)amp_enable);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_set_amp_enable() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
printf("call hackrf_set_amp_enable(%u)\n", amp_enable);
result = hackrf_set_amp_enable(device, (uint8_t)amp_enable);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_set_amp_enable() failed: %s (%d)\n", hackrf_error_name(result), result);
usage();
return EXIT_FAILURE;
}
printf("Stop with Ctrl-C\n");
while( (hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false) ) {
// Show something?
}
printf("Stop with Ctrl-C\n");
while( (hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false) ) {
// Show something?
}
result = hackrf_is_streaming(device);
if (do_exit) {
printf("\nUser cancel, exiting...\n");
} else {
printf("\nExiting... hackrf_is_streaming() result: %s (%d)\n", hackrf_error_name(result), result);
}
result = hackrf_is_streaming(device);
if (do_exit) {
printf("\nUser cancel, exiting...\n");
} else {
printf("\nExiting... hackrf_is_streaming() result: %s (%d)\n", hackrf_error_name(result), result);
}
if(device != NULL) {
result = hackrf_stop_tx(device);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_stop_tx() failed: %s (%d)\n", hackrf_error_name(result), result);
} else {
printf("hackrf_stop_tx() done\n");
}
if(device != NULL) {
result = hackrf_stop_tx(device);
if( result != HACKRF_SUCCESS ) {
printf("hackrf_stop_tx() failed: %s (%d)\n", hackrf_error_name(result), result);
} else {
printf("hackrf_stop_tx() done\n");
}
result = hackrf_close(device);
if( result != HACKRF_SUCCESS )
{
printf("hackrf_close() failed: %s (%d)\n", hackrf_error_name(result), result);
} else {
printf("hackrf_close() done\n");
}
result = hackrf_close(device);
if( result != HACKRF_SUCCESS )
{
printf("hackrf_close() failed: %s (%d)\n", hackrf_error_name(result), result);
} else {
printf("hackrf_close() done\n");
}
hackrf_exit();
printf("hackrf_exit() done\n");
}
hackrf_exit();
printf("hackrf_exit() done\n");
}
if(fd != NULL) {
fclose(fd);
fd = NULL;
printf("fclose(fd) done\n");
}
if(fd != NULL) {
fclose(fd);
fd = NULL;
printf("fclose(fd) done\n");
}
printf("exit\n");
return EXIT_SUCCESS;
printf("exit\n");
return EXIT_SUCCESS;
}

401
player/limeplayer.c Normal file
View File

@ -0,0 +1,401 @@
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <lime/LimeSuite.h>
#define EXIT_CODE_CONTROL_C (-3)
#define EXIT_CODE_NO_DEVICE (-2)
#define EXIT_CODE_LMS_OPEN (-1)
#define TX_FREQUENCY 1575420000.0
#define TX_SAMPLERATE 2500000.0
#define TX_BANDWIDTH 5000000.0
#define DEFAULT_ANTENNA 1 // antenna with BW [30MHz .. 2000MHz]
#define STRINGIFY2(X) #X
#define STRINGIFY(X) STRINGIFY2(X)
static int control_c_received = 0;
static void control_c_handler (int sig, siginfo_t *siginfo, void *context){
control_c_received = 1;
}
static void print_usage(const char *progname){
printf("Usage: %s [option] < file" "\n"
"\t" "-g <gain> or --gain <gain> with gain in [0.0 .. 1.0] set the so-called normalized RF gain in LimeSDR (default: 1.0 max RF power)" "\n"
"\t" "-c <channel> or --channel <channel> with channel either 0 or 1 (default: 0)" "\n"
"\t" "-a <antenna> or --antenna <antenna> with antenna in { 0, 1, 2 } (default:" STRINGIFY(DEFAULT_ANTENNA) ")" "\n"
"\t" "-i <index> or --index <index> select LimeSDR if multiple devices connected (default: 0)" "\n"
"\t" "-b <bits> or --bits <bits> select bit count in IQ sample in { 1, 8, 12, 16 }, (default: 16)" "\n"
"\t" "-s <samplerate> or --samplerate <samplerate> configure BB sample rate (default: " STRINGIFY(TX_SAMPLERATE) ")" "\n"
"\t" "-d <dynamic> --dynamic <dynamic> configure dynamic for the 1-bit mode (default: 2047, max 12-bit signed value supported by LimeSDR)" "\n"
"Example:" "\n"
"\t" "./limeplayer -s 1000000 -b 1 -d 1023 -g 0.1 < ../circle.1b.1M.bin" "\n", progname);
exit(0);
}
int main(int argc, char *const argv[]){
struct sigaction control_c;
memset(&control_c, 0, sizeof(control_c));
control_c.sa_sigaction = &control_c_handler;
/* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
control_c.sa_flags = SA_SIGINFO;
if (sigaction(SIGTERM, &control_c, NULL) < 0) {
perror ("sigaction");
return(EXIT_CODE_CONTROL_C);
}
if (sigaction(SIGQUIT, &control_c, NULL) < 0) {
perror ("sigaction");
return(EXIT_CODE_CONTROL_C);
}
if (sigaction(SIGINT, &control_c, NULL) < 0) {
perror ("sigaction");
return(EXIT_CODE_CONTROL_C);
}
int device_count = LMS_GetDeviceList(NULL);
if(device_count < 1){
return(EXIT_CODE_NO_DEVICE);
}
lms_info_str_t *device_list = malloc(sizeof(lms_info_str_t) * device_count);
device_count = LMS_GetDeviceList(device_list);
int i = 0;
while(i < device_count){
// printf("device[%d/%d]=%s" "\n", i + 1, device_count, device_list[i]);
i++;
}
double gain = 1.0;
int32_t antenna = DEFAULT_ANTENNA;
int32_t channel = 0;
int32_t index = 0;
int32_t bits = 16;
double sampleRate = TX_SAMPLERATE;
int32_t dynamic = 2047;
while (1) {
int option_index = 0;
static struct option long_options[] = {
{"gain", required_argument, 0, 'g' },
{"channel", required_argument, 0, 'c' },
{"antenna", required_argument, 0, 'a' },
{"index", required_argument, 0, 'i'},
{"bits", required_argument, 0, 'b'},
{"samplerate", required_argument, 0, 's'},
{"dynamic", required_argument, 0, 'd'},
{0, 0, 0, 0 }
};
int c = getopt_long(argc, argv, "g:c:a:i:s:b:d:", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
#if 1
fprintf(stderr, "option %s", long_options[option_index].name);
if (optarg)
fprintf(stderr, " with arg %s", optarg);
fprintf(stderr, "\n");
#endif
break;
case 'a':
antenna = strtol(optarg, NULL, 0);
break;
case 'b':
bits = strtol(optarg, NULL, 0);
break;
case 'c':
channel = strtol(optarg, NULL, 0);
break;
case 'g':
gain = strtod(optarg, NULL);
break;
case 'i':
index = strtol(optarg, NULL, 0);
break;
case 's':
sampleRate = strtod(optarg, NULL);
break;
case 'd':
dynamic = strtol(optarg, NULL, 0);
if(dynamic > 2047){
dynamic = 2047;
}
break;
default:
print_usage(argv[0]);
break;
}
}
// Use correct values
// Use existing device
if(index < 0){
index = 0;
}
if(index >= device_count){
index = 0;
}
printf("Using device index %d [%s]" "\n", index, device_list[index]);
// Normalized gain shall be in [0.0 .. 1.0]
if(gain < 0.0){
gain = 0.0;
}
if(gain > 1.0){
gain = 1.0;
}
printf("Using normalized gain %lf" "\n", gain);
lms_device_t *device = NULL;
if(LMS_Open(&device, device_list[index], NULL)){
return(EXIT_CODE_LMS_OPEN);
}
int lmsReset = LMS_Reset(device);
if(lmsReset){
printf("lmsReset %d(%s)" "\n", lmsReset, LMS_GetLastErrorMessage());
}
int lmsInit = LMS_Init(device);
if(lmsInit){
printf("lmsInit %d(%s)" "\n", lmsInit, LMS_GetLastErrorMessage());
}
int channel_count = LMS_GetNumChannels(device, LMS_CH_TX);
// printf("Tx channel count %d" "\n", channel_count);
if(channel < 0){
channel = 0;
}
if(channel >= channel_count){
channel = 0;
}
printf("Using channel %d" "\n", channel);
int antenna_count = LMS_GetAntennaList(device, LMS_CH_TX, channel, NULL);
// printf("TX%d Channel has %d antenna(ae)" "\n", channel, antenna_count);
lms_name_t antenna_name[antenna_count];
if(antenna_count > 0){
int i = 0;
lms_range_t antenna_bw[antenna_count];
LMS_GetAntennaList(device, LMS_CH_TX, channel, antenna_name);
for(i = 0 ; i < antenna_count ; i++){
LMS_GetAntennaBW(device, LMS_CH_TX, channel, i, antenna_bw + i);
// printf("Channel %d, antenna [%s] has BW [%lf .. %lf] (step %lf)" "\n", channel, antenna_name[i], antenna_bw[i].min, antenna_bw[i].max, antenna_bw[i].step);
}
}
if(antenna < 0){
antenna = DEFAULT_ANTENNA;
}
if(antenna >= antenna_count){
antenna = DEFAULT_ANTENNA;
}
// LMS_SetAntenna(device, LMS_CH_TX, channel, antenna); // SetLOFrequency should take care of selecting the proper antenna
LMS_SetNormalizedGain(device, LMS_CH_TX, channel, gain);
// Disable all other channels
LMS_EnableChannel(device, LMS_CH_TX, 1 - channel, false);
LMS_EnableChannel(device, LMS_CH_RX, 0, true); /* LimeSuite bug workaround (needed since LimeSuite git rev 52d6129 - or v18.06.0) */
LMS_EnableChannel(device, LMS_CH_RX, 1, false);
// Enable our Tx channel
LMS_EnableChannel(device, LMS_CH_TX, channel, true);
int setLOFrequency = LMS_SetLOFrequency(device, LMS_CH_TX, channel, TX_FREQUENCY);
if(setLOFrequency){
printf("setLOFrequency(%lf)=%d(%s)" "\n", TX_FREQUENCY, setLOFrequency, LMS_GetLastErrorMessage());
}
#ifdef __USE_LPF__
lms_range_t LPFBWRange;
LMS_GetLPFBWRange(device, LMS_CH_TX, &LPFBWRange);
// printf("TX%d LPFBW [%lf .. %lf] (step %lf)" "\n", channel, LPFBWRange.min, LPFBWRange.max, LPFBWRange.step);
double LPFBW = TX_BANDWIDTH;
if(LPFBW < LPFBWRange.min){
LPFBW = LPFBWRange.min;
}
if(LPFBW > LPFBWRange.max){
LPFBW = LPFBWRange.min;
}
int setLPFBW = LMS_SetLPFBW(device, LMS_CH_TX, channel, LPFBW);
if(setLPFBW){
printf("setLPFBW(%lf)=%d(%s)" "\n", LPFBW, setLPFBW, LMS_GetLastErrorMessage());
}
int enableLPF = LMS_SetLPF(device, LMS_CH_TX, channel, true);
if(enableLPF){
printf("enableLPF=%d(%s)" "\n", enableLPF, LMS_GetLastErrorMessage());
}
#endif
lms_range_t sampleRateRange;
int getSampleRateRange = LMS_GetSampleRateRange(device, LMS_CH_TX, &sampleRateRange);
if(getSampleRateRange){
printf("getSampleRateRange=%d(%s)" "\n", getSampleRateRange, LMS_GetLastErrorMessage());
}else{
// printf("sampleRateRange [%lf MHz.. %lf MHz] (step=%lf Hz)" "\n", sampleRateRange.min / 1e6, sampleRateRange.max / 1e6, sampleRateRange.step);
}
printf("Set sample rate to %lf ..." "\n", sampleRate);
int setSampleRate = LMS_SetSampleRate(device, sampleRate, 0);
if(setSampleRate){
printf("setSampleRate=%d(%s)" "\n", setSampleRate, LMS_GetLastErrorMessage());
}
double actualHostSampleRate = 0.0;
double actualRFSampleRate = 0.0;
int getSampleRate = LMS_GetSampleRate(device, LMS_CH_TX, channel, &actualHostSampleRate, &actualRFSampleRate);
if(getSampleRate){
printf("getSampleRate=%d(%s)" "\n", getSampleRate, LMS_GetLastErrorMessage());
}else{
printf("actualRate %lf (Host) / %lf (RF)" "\n", actualHostSampleRate, actualRFSampleRate);
}
printf("Calibrating ..." "\n");
int calibrate = LMS_Calibrate(device, LMS_CH_TX, channel, TX_BANDWIDTH, 0);
if(calibrate){
printf("calibrate=%d(%s)" "\n", calibrate, LMS_GetLastErrorMessage());
}
printf("Setup TX stream ..." "\n");
lms_stream_t tx_stream = {.channel = channel, .fifoSize = 1024*1024, .throughputVsLatency = 0.5, .isTx = true, .dataFmt = LMS_FMT_I12};
int setupStream = LMS_SetupStream(device, &tx_stream);
if(setupStream){
printf("setupStream=%d(%s)" "\n", setupStream, LMS_GetLastErrorMessage());
}
struct s16iq_sample_s {
signed short int i;
signed short int q;
};
int nSamples = (int)sampleRate / 100;
struct s16iq_sample_s *sampleBuffer = (struct s16iq_sample_s*)malloc(sizeof(struct s16iq_sample_s) * nSamples);
LMS_StartStream(&tx_stream);
int loop = 0;
if((12 == bits) || (16 == bits)){
// File contains interleaved 16-bit IQ values, either with only 12-bit data, or with 16-bit data
while((0 == control_c_received) && fread(sampleBuffer, sizeof(struct s16iq_sample_s), nSamples, stdin)){
loop++;
if(0 == (loop % 100)){
struct timeval tv;
gettimeofday(&tv, NULL);
printf("gettimeofday()=> %ld:%06ld ; ", tv.tv_sec, tv.tv_usec);
lms_stream_status_t status;
LMS_GetStreamStatus(&tx_stream, &status); //Obtain TX stream stats
printf("TX rate:%lf MB/s" "\n", status.linkRate / 1e6);
}
if(16 == bits){
// Scale down to 12-bit
// Quick and dirty, so -1 (0xFFFF) to -15 (0xFFF1) scale down to -1 instead of 0
int i = 0;
while(i < nSamples){
sampleBuffer[i].i >>= 4;
sampleBuffer[i].q >>= 4;
i++;
}
}
int sendStream = LMS_SendStream(&tx_stream, sampleBuffer, nSamples, NULL, 1000);
if(sendStream < 0){
printf("sendStream %d(%s)" "\n", sendStream, LMS_GetLastErrorMessage());
}
}
}else if(8 == bits){
// File contains interleaved signed 8-bit IQ values
struct s8iq_sample_s {
signed char i;
signed char q;
};
struct s8iq_sample_s *fileSamples = (struct s8iq_sample_s*)malloc(sizeof(struct s8iq_sample_s) * nSamples);
while((0 == control_c_received) && fread(fileSamples, sizeof(struct s8iq_sample_s), nSamples, stdin)){
loop++;
if(0 == (loop % 100)){
struct timeval tv;
gettimeofday(&tv, NULL);
printf("gettimeofday()=> %ld:%06ld ; ", tv.tv_sec, tv.tv_usec);
lms_stream_status_t status;
LMS_GetStreamStatus(&tx_stream, &status); //Obtain TX stream stats
printf("TX rate:%lf MB/s" "\n", status.linkRate / 1e6);
}
// Up-Scale to 12-bit
int i = 0;
while(i < nSamples){
sampleBuffer[i].i = (fileSamples[i].i << 4);
sampleBuffer[i].q = (fileSamples[i].q << 4);
i++;
}
int sendStream = LMS_SendStream(&tx_stream, sampleBuffer, nSamples, NULL, 1000);
if(sendStream < 0){
printf("sendStream %d(%s)" "\n", sendStream, LMS_GetLastErrorMessage());
}
}
free(fileSamples);
}else if(1 == bits){
// File contains interleaved signed 1-bit IQ values
// Each byte is IQIQIQIQ
int16_t expand_lut[256][8];
int i, j;
for (i=0; i<256; i++){
for (j=0; j<8; j++){
expand_lut[i][j] = ((i>>(7-j))&0x1)?dynamic:-dynamic;
}
}
printf("1-bit mode: using dynamic=%d" "\n", dynamic);
// printf("sizeof(expand_lut[][])=%d, sizeof(expand_lut[0])=%d" "\n", sizeof(expand_lut), sizeof(expand_lut[0]));
int8_t *fileBuffer = (int8_t*)malloc(sizeof(int8_t) * nSamples);
while((0 == control_c_received) && fread(fileBuffer, sizeof(int8_t), nSamples / 4, stdin)){
loop++;
if(0 == (loop % 100)){
struct timeval tv;
gettimeofday(&tv, NULL);
printf("gettimeofday()=> %ld:%06ld ; ", tv.tv_sec, tv.tv_usec);
lms_stream_status_t status;
LMS_GetStreamStatus(&tx_stream, &status); //Obtain TX stream stats
printf("TX rate:%lf MB/s" "\n", status.linkRate / 1e6);
}
// Expand
int src = 0;
int dst = 0;
while(src < (nSamples / 4)){
memcpy(sampleBuffer + dst, expand_lut + fileBuffer[src], sizeof(expand_lut[0]));
dst += 4;
src++;
}
int sendStream = LMS_SendStream(&tx_stream, sampleBuffer, nSamples, NULL, 1000);
if(sendStream < 0){
printf("sendStream %d(%s)" "\n", sendStream, LMS_GetLastErrorMessage());
}
}
free(fileBuffer);
}
LMS_StopStream(&tx_stream);
LMS_DestroyStream(device, &tx_stream);
free(sampleBuffer);
LMS_EnableChannel(device, LMS_CH_TX, channel, false);
LMS_Close(device);
if(control_c_received){
return(EXIT_CODE_CONTROL_C);
}
return(0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) 1987, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "getopt.h"
#include <string.h>
#include <stdio.h>
int opterr = 1, /* if error message should be printed */
optind = 1, /* index into parent argv vector */
optopt, /* character checked for validity */
optreset; /* reset getopt */
char *optarg; /* argument associated with option */
#define BADCH (int)'?'
#define BADARG (int)':'
#define EMSG ""
/*
* getopt --
* Parse argc/argv argument vector.
*
* Note: Unlike GNU getopt(), after a non-option argument, all further
* arguments are considered also non-options. This is similar to
* the way non-GNU Unix systems work.
*/
int getopt(int nargc, char * const nargv[], const char *ostr)
{
static char *place = EMSG; /* option letter processing */
const char *oli; /* option letter list index */
if (optreset || !*place) { /* update scanning pointer */
optreset = 0;
if (optind >= nargc || *(place = nargv[optind]) != '-') {
place = EMSG;
return (-1);
}
if (place[1] && *++place == '-') { /* found "--" */
++optind;
place = EMSG;
return (-1);
}
} /* option letter okay? */
if ((optopt = (int)*place++) == (int)':' ||
!(oli = strchr(ostr, optopt))) {
/*
* if the user didn't specify '-' as an option,
* assume it means -1.
*/
if (optopt == (int)'-')
return (-1);
if (!*place)
++optind;
if (opterr && *ostr != ':')
(void)printf("illegal option -- %c\n", optopt);
return (BADCH);
}
if (*++oli != ':') { /* don't need argument */
optarg = NULL;
if (!*place)
++optind;
}
else { /* need an argument */
if (*place) /* no white space */
optarg = place;
else if (nargc <= ++optind) { /* no arg */
place = EMSG;
if (*ostr == ':')
return (BADARG);
if (opterr)
(void)printf("option requires an argument -- %c\n", optopt);
return (BADCH);
}
else /* white space */
optarg = nargv[optind];
place = EMSG;
++optind;
}
return (optopt); /* dump back option letter */
}

View File

@ -0,0 +1,9 @@
#ifndef GETOPT_H
#define GETOPT_H
extern char *optarg;
extern int optind;
int getopt(int nargc, char * const nargv[], const char *ostr) ;
#endif

View File

@ -0,0 +1,409 @@
#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include "getopt.h"
#else
#include <unistd.h>
#endif
#include <lime/LimeSuite.h>
// Configration Properties:
// C/C++ --> General --> Additional Include Directories: C:\Program Files\PothosSDR\include
// Linker --> General --> Additional Library Directories: C:\Program Files\PothosSDR\lib
// Linker --> Input --> Additional Dependencies: LimeSuite.lib
#define EXIT_CODE_NO_DEVICE (-2)
#define EXIT_CODE_LMS_OPEN (-1)
#define TX_FREQUENCY 1575420000.0
#define TX_SAMPLERATE 2500000.0
#define TX_BANDWIDTH 5000000.0
#define DEFAULT_ANTENNA 1 // antenna with BW [30MHz .. 2000MHz]
typedef struct timeval {
long tv_sec;
long tv_usec;
} timeval;
int gettimeofday(struct timeval * tp, struct timezone * tzp)
{
// Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's
// This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC)
// until 00:00:00 January 1, 1970
static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL);
SYSTEMTIME system_time;
FILETIME file_time;
uint64_t time;
GetSystemTime(&system_time);
SystemTimeToFileTime(&system_time, &file_time);
time = ((uint64_t)file_time.dwLowDateTime);
time += ((uint64_t)file_time.dwHighDateTime) << 32;
tp->tv_sec = (long)((time - EPOCH) / 10000000L);
tp->tv_usec = (long)(system_time.wMilliseconds * 1000);
return 0;
}
void usage(const char *progname)
{
fprintf(stderr, "Usage: %s [options]\n"
" -f <file> select IQ sample data file to transmit (required)\n"
" -g <gain> with gain in [0.0 .. 1.0] set the so-called normalized RF gain in LimeSDR (default: 1.0 max RF power)\n"
" -c <channel> with channel either 0 or 1 (default: 0)\n"
" -a <antenna> with antenna in { 0, 1, 2 } (default: 1)\n"
" -i <index> select LimeSDR if multiple devices connected (default: 0)\n"
" -b <bits> select bit count in IQ sample in { 1, 8, 12, 16 }, (default: 16)\n"
" -s <samplerate> configure baseband sample rate (default: 2500000.0)\n"
" -d <dynamic> configure dynamic for the 1-bit mode (default: 2047, max 12-bit signed value supported by LimeSDR)\n",
progname);
return;
}
int main(int argc, char *const argv[])
{
if (argc<3) {
usage(argv[0]);
exit(1);
}
int device_count = LMS_GetDeviceList(NULL);
if (device_count < 1)
{
fprintf(stderr, "ERROR: No device was found.\n");
return(EXIT_CODE_NO_DEVICE);
}
lms_info_str_t *device_list = malloc(sizeof(lms_info_str_t) * device_count);
device_count = LMS_GetDeviceList(device_list);
int i = 0;
while (i < device_count)
{
printf("device[%d/%d]=%s" "\n", i + 1, device_count, device_list[i]);
i++;
}
double gain = 1.0;
int32_t antenna = DEFAULT_ANTENNA;
int32_t channel = 0;
int32_t index = 0;
int32_t bits = 16;
double sampleRate = TX_SAMPLERATE;
int32_t dynamic = 2047;
int result;
char txfile[128];
txfile[0] = 0; // Empty TX file name
while ((result = getopt(argc, argv, "f:g:c:a:i:b:s:d:")) != -1)
{
switch (result)
{
case 'f':
strcpy(txfile, optarg);
break;
case 'a':
antenna = strtol(optarg, NULL, 0);
break;
case 'b':
bits = strtol(optarg, NULL, 0);
break;
case 'c':
channel = strtol(optarg, NULL, 0);
break;
case 'g':
gain = strtod(optarg, NULL);
break;
case 'i':
index = strtol(optarg, NULL, 0);
break;
case 's':
sampleRate = strtod(optarg, NULL);
break;
case 'd':
dynamic = strtol(optarg, NULL, 0);
if (dynamic > 2047)
dynamic = 2047;
break;
case '?':
usage(argv[0]);
exit(1);
default:
break;
}
}
// Open TX file.
FILE *fp;
if (txfile[0] == 0)
{
printf("ERROR: I/Q sampling data file is not specified.\n");
exit(1);
}
fp = fopen(txfile, "rb");
if (fp == NULL) {
fprintf(stderr, "ERROR: Failed to open TX file: %s\n", argv[1]);
exit(1);
}
// Use existing device
if (index < 0)
index = 0;
if (index >= device_count)
index = 0;
printf("Using device index %d [%s]" "\n", index, device_list[index]);
// Normalized gain shall be in [0.0 .. 1.0]
if (gain < 0.0)
gain = 0.0;
if (gain > 1.0)
gain = 1.0;
printf("Using normalized gain %lf" "\n", gain);
// Open device and initialize.
lms_device_t *device = NULL;
if (LMS_Open(&device, device_list[index], NULL))
return(EXIT_CODE_LMS_OPEN);
int lmsReset = LMS_Reset(device);
if (lmsReset)
printf("lmsReset %d(%s)" "\n", lmsReset, LMS_GetLastErrorMessage());
int lmsInit = LMS_Init(device);
if (lmsInit)
printf("lmsInit %d(%s)" "\n", lmsInit, LMS_GetLastErrorMessage());
// Select channel.
int channel_count = LMS_GetNumChannels(device, LMS_CH_TX);
// printf("Tx channel count %d" "\n", channel_count);
if (channel < 0)
channel = 0;
if (channel >= channel_count)
channel = 0;
printf("Using channel %d" "\n", channel);
// Select antenna.
int antenna_count = LMS_GetAntennaList(device, LMS_CH_TX, channel, NULL);
printf("TX%d Channel has %d antenna(ae)" "\n", channel, antenna_count);
lms_name_t *antenna_name = malloc(sizeof(lms_name_t) * antenna_count);
if (antenna_count > 0)
{
int i = 0;
lms_range_t *antenna_bw = malloc(sizeof(lms_range_t) * antenna_count);
LMS_GetAntennaList(device, LMS_CH_TX, channel, antenna_name);
for (i = 0; i < antenna_count; i++)
{
LMS_GetAntennaBW(device, LMS_CH_TX, channel, i, antenna_bw + i);
printf("Channel %d, antenna [%s] has BW [%lf .. %lf] (step %lf)" "\n", channel, antenna_name[i], antenna_bw[i].min, antenna_bw[i].max, antenna_bw[i].step);
}
}
if (antenna < 0)
antenna = DEFAULT_ANTENNA;
if (antenna >= antenna_count)
antenna = DEFAULT_ANTENNA;
// LMS_SetAntenna(device, LMS_CH_TX, channel, antenna); // SetLOFrequency should take care of selecting the proper antenna
LMS_SetNormalizedGain(device, LMS_CH_TX, channel, gain);
// Disable all other channels
LMS_EnableChannel(device, LMS_CH_TX, 1 - channel, false);
LMS_EnableChannel(device, LMS_CH_RX, 0, false);
LMS_EnableChannel(device, LMS_CH_RX, 1, false);
// Enable our Tx channel
LMS_EnableChannel(device, LMS_CH_TX, channel, true);
int setLOFrequency = LMS_SetLOFrequency(device, LMS_CH_TX, channel, TX_FREQUENCY);
if (setLOFrequency)
printf("setLOFrequency(%lf)=%d(%s)" "\n", TX_FREQUENCY, setLOFrequency, LMS_GetLastErrorMessage());
// Set sample rate.
lms_range_t sampleRateRange;
int getSampleRateRange = LMS_GetSampleRateRange(device, LMS_CH_TX, &sampleRateRange);
if (getSampleRateRange)
printf("getSampleRateRange=%d(%s)" "\n", getSampleRateRange, LMS_GetLastErrorMessage());
else
{
printf("sampleRateRange [%lf MHz.. %lf MHz] (step=%lf Hz)" "\n", sampleRateRange.min / 1e6, sampleRateRange.max / 1e6, sampleRateRange.step);
}
printf("Set sample rate to %lf ..." "\n", sampleRate);
int setSampleRate = LMS_SetSampleRate(device, sampleRate, 0);
if (setSampleRate)
printf("setSampleRate=%d(%s)" "\n", setSampleRate, LMS_GetLastErrorMessage());
double actualHostSampleRate = 0.0;
double actualRFSampleRate = 0.0;
int getSampleRate = LMS_GetSampleRate(device, LMS_CH_TX, channel, &actualHostSampleRate, &actualRFSampleRate);
if (getSampleRate)
printf("getSampleRate=%d(%s)" "\n", getSampleRate, LMS_GetLastErrorMessage());
else
printf("actualRate %lf (Host) / %lf (RF)" "\n", actualHostSampleRate, actualRFSampleRate);
printf("Calibrating ..." "\n");
int calibrate = LMS_Calibrate(device, LMS_CH_TX, channel, TX_BANDWIDTH, 0);
if (calibrate)
printf("calibrate=%d(%s)" "\n", calibrate, LMS_GetLastErrorMessage());
printf("Setup TX stream ..." "\n");
lms_stream_t tx_stream = { .channel = channel,.fifoSize = 1024 * 1024,.throughputVsLatency = 0.5,.isTx = true,.dataFmt = LMS_FMT_I12 };
int setupStream = LMS_SetupStream(device, &tx_stream);
if (setupStream)
printf("setupStream=%d(%s)" "\n", setupStream, LMS_GetLastErrorMessage());
struct s16iq_sample_s {
signed short int i;
signed short int q;
};
int nSamples = (int)sampleRate / 100;
struct s16iq_sample_s *sampleBuffer = (struct s16iq_sample_s*)malloc(sizeof(struct s16iq_sample_s) * nSamples);
LMS_StartStream(&tx_stream);
int loop = 0;
if ((12 == bits) || (16 == bits))
{
// File contains interleaved 16-bit IQ values, either with only 12-bit data, or with 16-bit data
while (fread(sampleBuffer, sizeof(struct s16iq_sample_s), nSamples, fp))
{
loop++;
if (0 == (loop % 100))
{
struct timeval tv;
gettimeofday(&tv, NULL);
printf("gettimeofday()=> %ld:%06ld ; ", tv.tv_sec, tv.tv_usec);
lms_stream_status_t status;
LMS_GetStreamStatus(&tx_stream, &status); //Obtain TX stream stats
printf("TX rate:%lf MB/s" "\n", status.linkRate / 1e6);
}
if (16 == bits)
{
// Scale down to 12-bit
// Quick and dirty, so -1 (0xFFFF) to -15 (0xFFF1) scale down to -1 instead of 0
int i = 0;
while (i < nSamples) {
sampleBuffer[i].i >>= 4;
sampleBuffer[i].q >>= 4;
i++;
}
}
int sendStream = LMS_SendStream(&tx_stream, sampleBuffer, nSamples, NULL, 1000);
if (sendStream < 0)
printf("sendStream %d(%s)" "\n", sendStream, LMS_GetLastErrorMessage());
}
}
else if (8 == bits)
{
// File contains interleaved signed 8-bit IQ values
struct s8iq_sample_s {
signed char i;
signed char q;
};
struct s8iq_sample_s *fileSamples = (struct s8iq_sample_s*)malloc(sizeof(struct s8iq_sample_s) * nSamples);
while (fread(fileSamples, sizeof(struct s8iq_sample_s), nSamples, fp))
{
loop++;
if (0 == (loop % 100))
{
struct timeval tv;
gettimeofday(&tv, NULL);
printf("gettimeofday()=> %ld:%06ld ; ", tv.tv_sec, tv.tv_usec);
lms_stream_status_t status;
LMS_GetStreamStatus(&tx_stream, &status); //Obtain TX stream stats
printf("TX rate:%lf MB/s" "\n", status.linkRate / 1e6);
}
// Up-Scale to 12-bit
int i = 0;
while (i < nSamples)
{
sampleBuffer[i].i = (fileSamples[i].i << 4);
sampleBuffer[i].q = (fileSamples[i].q << 4);
i++;
}
int sendStream = LMS_SendStream(&tx_stream, sampleBuffer, nSamples, NULL, 1000);
if (sendStream < 0)
printf("sendStream %d(%s)" "\n", sendStream, LMS_GetLastErrorMessage());
}
free(fileSamples);
}
else if (1 == bits)
{
// File contains interleaved signed 1-bit IQ values
// Each byte is IQIQIQIQ
int16_t expand_lut[256][8];
int i, j;
for (i = 0; i<256; i++)
{
for (j = 0; j<8; j++)
expand_lut[i][j] = ((i >> (7 - j)) & 0x1) ? dynamic : -dynamic;
}
printf("1-bit mode: using dynamic=%d" "\n", dynamic);
// printf("sizeof(expand_lut[][])=%d, sizeof(expand_lut[0])=%d" "\n", sizeof(expand_lut), sizeof(expand_lut[0]));
int8_t *fileBuffer = (int8_t*)malloc(sizeof(int8_t) * nSamples);
while (fread(fileBuffer, sizeof(int8_t), nSamples / 4, fp))
{
loop++;
if (0 == (loop % 100))
{
struct timeval tv;
gettimeofday(&tv, NULL);
printf("gettimeofday()=> %ld:%06ld ; ", tv.tv_sec, tv.tv_usec);
lms_stream_status_t status;
LMS_GetStreamStatus(&tx_stream, &status); //Obtain TX stream stats
printf("TX rate:%lf MB/s" "\n", status.linkRate / 1e6);
}
// Expand
int src = 0;
int dst = 0;
while (src < (nSamples / 4))
{
memcpy(sampleBuffer + dst, expand_lut + fileBuffer[src], sizeof(expand_lut[0]));
dst += 4;
src++;
}
int sendStream = LMS_SendStream(&tx_stream, sampleBuffer, nSamples, NULL, 1000);
if (sendStream < 0)
printf("sendStream %d(%s)" "\n", sendStream, LMS_GetLastErrorMessage());
}
free(fileBuffer);
}
LMS_StopStream(&tx_stream);
LMS_DestroyStream(device, &tx_stream);
free(sampleBuffer);
printf("Closing device...\n");
LMS_EnableChannel(device, LMS_CH_TX, channel, false);
LMS_Close(device);
// Close TX file
fclose(fp);
printf("Done.\n");
return(0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

245
player/plutoplayer.c Normal file
View File

@ -0,0 +1,245 @@
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <iio.h>
#include <ad9361.h>
#define NOTUSED(V) ((void) V)
#define MHZ(x) ((long long)(x*1000000.0 + .5))
#define GHZ(x) ((long long)(x*1000000000.0 + .5))
#define NUM_SAMPLES 2600000
#define BUFFER_SIZE (NUM_SAMPLES * 2 * sizeof(int16_t))
struct stream_cfg {
long long bw_hz; // Analog banwidth in Hz
long long fs_hz; // Baseband sample rate in Hz
long long lo_hz; // Local oscillator frequency in Hz
const char* rfport; // Port name
double gain_db; // Hardware gain
};
static void usage() {
fprintf(stderr, "Usage: plutoplayer [options]\n"
" -t <filename> Transmit data from file (required)\n"
" -a <attenuation> Set TX attenuation [dB] (default -20.0)\n"
" -b <bw> Set RF bandwidth [MHz] (default 5.0)\n"
" -u <uri> ADALM-Pluto URI\n"
" -n <network> ADALM-Pluto network IP or hostname (default pluto.local)\n");
return;
}
static bool stop = false;
static void handle_sig(int sig)
{
NOTUSED(sig);
stop = true;
}
static char* readable_fs(double size, char *buf, size_t buf_size) {
int i = 0;
const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
while (size > 1024) {
size /= 1024;
i++;
}
snprintf(buf, buf_size, "%.*f %s", i, size, units[i]);
return buf;
}
/*
*
*/
int main(int argc, char** argv) {
char buf[1024];
int opt;
const char* path = NULL;
struct stream_cfg txcfg;
FILE *fp = NULL;
const char *uri = NULL;
const char *ip = NULL;
// TX stream default config
txcfg.bw_hz = MHZ(3.0); // 3.0 MHz RF bandwidth
txcfg.fs_hz = MHZ(2.6); // 2.6 MS/s TX sample rate
txcfg.lo_hz = GHZ(1.575420); // 1.57542 GHz RF frequency
txcfg.rfport = "A";
txcfg.gain_db = -20.0;
struct iio_context *ctx = NULL;
struct iio_device *tx = NULL;
struct iio_device *phydev = NULL;
struct iio_channel *tx0_i = NULL;
struct iio_channel *tx0_q = NULL;
struct iio_buffer *tx_buffer = NULL;
while ((opt = getopt(argc, argv, "t:a:b:n:u:")) != EOF) {
switch (opt) {
case 't':
path = optarg;
break;
case 'a':
txcfg.gain_db = atof(optarg);
if(txcfg.gain_db > 0.0) txcfg.gain_db = 0.0;
if(txcfg.gain_db < -80.0) txcfg.gain_db = -80.0;
break;
case 'b':
txcfg.bw_hz = MHZ(atof(optarg));
if(txcfg.bw_hz > MHZ(5.0)) txcfg.bw_hz = MHZ(5.0);
if(txcfg.bw_hz < MHZ(1.0)) txcfg.bw_hz = MHZ(1.0);
break;
case 'u':
uri = optarg;
break;
case 'n':
ip = optarg;
break;
default:
printf("Unknown argument '-%c %s'\n", opt, optarg);
usage();
return EXIT_FAILURE;
}
}
signal(SIGINT, handle_sig);
if( path == NULL ) {
printf("Specify a path to a file to transmit\n");
usage();
return EXIT_FAILURE;
}
fp = fopen(path, "rb");
if (fp==NULL) {
fprintf(stderr, "ERROR: Failed to open TX file: %s\n", path);
return EXIT_FAILURE;
}
fseek(fp, 0L, SEEK_END);
size_t sz = ftell(fp);
fseek(fp, 0L, SEEK_SET);
readable_fs((double)sz, buf, sizeof(buf));
printf("* Transmit file size: %s\n", buf);
printf("* Acquiring IIO context\n");
ctx = iio_create_default_context();
if (ctx == NULL) {
if(ip != NULL) {
ctx = iio_create_network_context(ip);
} else if (uri != NULL) {
ctx = iio_create_context_from_uri(uri);
} else {
ctx = iio_create_network_context("pluto.local");
}
}
if (ctx == NULL) {
iio_strerror(errno, buf, sizeof(buf));
fprintf(stderr, "Failed creating IIO context: %s\n", buf);
return false;
}
struct iio_scan_context *scan_ctx;
struct iio_context_info **info;
scan_ctx = iio_create_scan_context(NULL, 0);
if (scan_ctx) {
int info_count = iio_scan_context_get_info_list(scan_ctx, &info);
if(info_count > 0) {
printf("* Found %s\n", iio_context_info_get_description(info[0]));
iio_context_info_list_free(info);
}
iio_scan_context_destroy(scan_ctx);
}
printf("* Acquiring devices\n");
int device_count = iio_context_get_devices_count(ctx);
if (!device_count) {
fprintf(stderr, "No supported PLUTOSDR devices found.\n");
goto error_exit;
}
fprintf(stderr, "* Context has %d device(s).\n", device_count);
printf("* Acquiring TX device\n");
tx = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc");
if (tx == NULL) {
iio_strerror(errno, buf, sizeof(buf));
fprintf(stderr, "Error opening PLUTOSDR TX device: %s\n", buf);
goto error_exit;
}
iio_device_set_kernel_buffers_count(tx, 8);
phydev = iio_context_find_device(ctx, "ad9361-phy");
struct iio_channel* phy_chn = iio_device_find_channel(phydev, "voltage0", true);
iio_channel_attr_write(phy_chn, "rf_port_select", txcfg.rfport);
iio_channel_attr_write_longlong(phy_chn, "rf_bandwidth", txcfg.bw_hz);
iio_channel_attr_write_longlong(phy_chn, "sampling_frequency", txcfg.fs_hz);
iio_channel_attr_write_double(phy_chn, "hardwaregain", txcfg.gain_db);
iio_channel_attr_write_bool(
iio_device_find_channel(phydev, "altvoltage0", true)
, "powerdown", true); // Turn OFF RX LO
iio_channel_attr_write_longlong(
iio_device_find_channel(phydev, "altvoltage1", true)
, "frequency", txcfg.lo_hz); // Set TX LO frequency
printf("* Initializing streaming channels\n");
tx0_i = iio_device_find_channel(tx, "voltage0", true);
if (!tx0_i)
tx0_i = iio_device_find_channel(tx, "altvoltage0", true);
tx0_q = iio_device_find_channel(tx, "voltage1", true);
if (!tx0_q)
tx0_q = iio_device_find_channel(tx, "altvoltage1", true);
printf("* Enabling IIO streaming channels\n");
iio_channel_enable(tx0_i);
iio_channel_enable(tx0_q);
ad9361_set_bb_rate(iio_context_find_device(ctx, "ad9361-phy"), txcfg.fs_hz);
printf("* Creating TX buffer\n");
tx_buffer = iio_device_create_buffer(tx, NUM_SAMPLES, false);
if (!tx_buffer) {
fprintf(stderr, "Could not create TX buffer.\n");
goto error_exit;
}
iio_channel_attr_write_bool(
iio_device_find_channel(iio_context_find_device(ctx, "ad9361-phy"), "altvoltage1", true)
, "powerdown", false); // Turn ON TX LO
int32_t ntx = 0;
short *ptx_buffer = (short *)iio_buffer_start(tx_buffer);
printf("* Transmit starts...\n");
// Keep writing samples while there is more data to send and no failures have occurred.
while (!feof(fp) && !stop) {
fread(ptx_buffer, sizeof(short), BUFFER_SIZE / sizeof(short),fp);
// Schedule TX buffer
ntx = iio_buffer_push(tx_buffer);
if (ntx < 0) {
printf("Error pushing buf %d\n", (int) ntx);
break;
}
}
printf("Done.\n");
error_exit:
fclose(fp);
iio_channel_attr_write_bool(
iio_device_find_channel(iio_context_find_device(ctx, "ad9361-phy"), "altvoltage1", true)
, "powerdown", true); // Turn OFF TX LO
if (tx_buffer) { iio_buffer_destroy(tx_buffer); }
if (tx0_i) { iio_channel_disable(tx0_i); }
if (tx0_q) { iio_channel_disable(tx0_q); }
if (ctx) { iio_context_destroy(ctx); }
return EXIT_SUCCESS;
}