516 lines
10 KiB
C++
516 lines
10 KiB
C++
/*
|
|
* QO100 Transceiver based on ADALM-PLUTO and Raspi/Odroid... SBCs
|
|
* ==================
|
|
* Author: DJ0ABR
|
|
*
|
|
* (c) DJ0ABR
|
|
* www.dj0abr.de
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include "qo100trx.h"
|
|
|
|
|
|
|
|
char plutoid[100] = {"ip:192.168.20.25"};
|
|
//char plutoid[100];
|
|
|
|
char gui_ip[20] = {"127.127.0.1"};
|
|
int udprxsock = 0;
|
|
uint8_t audioloop = 0;
|
|
uint8_t rfloop = 0;
|
|
uint8_t ptt = 0;
|
|
uint8_t lastptt = 0;
|
|
uint8_t mute = 0;
|
|
char pbdevname[101] = {0};
|
|
char capdevname[101] = {0};
|
|
int newaudiodevs = 0;
|
|
int compressor = 0;
|
|
int gotPlutoID = 0;
|
|
int rxfilter = 3;
|
|
int txfilter = 3;
|
|
int rxmute = 0;
|
|
int refoffset = 0;
|
|
int resetqrgs = 0;
|
|
int beaconlock = 0;
|
|
int fftspeed = 0;
|
|
int audiohighpass = 0;
|
|
int micboost = 1; // Multiplikator for TX audio
|
|
int agcvalue = 32000; // should never exeed 16bit-signed (32768), buts need some reserve
|
|
int txpower = 0;
|
|
int recpb = 0; // 0=idle, 1=rec, 2=pb
|
|
int sendtone = 0;
|
|
int extpttinput_enabled = 0;
|
|
int mute_enabled = 0;
|
|
int pttmode = 0;
|
|
|
|
// fifos to send/receive samples with pluto run thread
|
|
int RXfifo;
|
|
int TXfifo;
|
|
int FFTfifo;
|
|
int CWfifo;
|
|
|
|
int pbidx = -1, capidx = -1;
|
|
|
|
void udprxfunc(uint8_t *pdata, int len, struct sockaddr_in* sender)
|
|
{
|
|
//printf("UDP command from GUI: %d: %d\n",pdata[0],pdata[1]);
|
|
|
|
if(pdata[0] == 0)
|
|
{
|
|
// set RX offset
|
|
uint32_t off = pdata[1];
|
|
off <<= 8;
|
|
off |= pdata[2];
|
|
off <<= 8;
|
|
off |= pdata[3];
|
|
off <<= 8;
|
|
off |= pdata[4];
|
|
|
|
//printf("RX offset: %d\n",off);
|
|
|
|
RXoffsetfreq = off;
|
|
|
|
// set TX offset
|
|
off = pdata[5];
|
|
off <<= 8;
|
|
off |= pdata[6];
|
|
off <<= 8;
|
|
off |= pdata[7];
|
|
off <<= 8;
|
|
off |= pdata[8];
|
|
|
|
//printf("TX offset: %d\n",off);
|
|
|
|
TXoffsetfreq = off;
|
|
}
|
|
|
|
if(pdata[0] == 3)
|
|
audioloop = pdata[1];
|
|
|
|
if(pdata[0] == 4)
|
|
{
|
|
ptt = pdata[1];
|
|
if(ptt && lastptt == 0)
|
|
{
|
|
// switch to TX mode
|
|
setSendtone(0);// never start with a test tone after pressing PTT
|
|
io_fifo_clear(capidx);
|
|
fifo_clear(TXfifo);
|
|
set_ptt();
|
|
}
|
|
|
|
if(ptt==0 && lastptt)
|
|
{
|
|
// switch to TX mode
|
|
release_ptt();
|
|
}
|
|
|
|
lastptt = ptt;
|
|
}
|
|
|
|
if(pdata[0] == 5)
|
|
{
|
|
rfloop = pdata[1];
|
|
if(rfloop)
|
|
{
|
|
setRXfrequency((long long)TX_FREQ);
|
|
}
|
|
else
|
|
{
|
|
setRXfrequency((long long)RX_FREQ);
|
|
}
|
|
}
|
|
|
|
if(pdata[0] == 7)
|
|
{
|
|
memcpy(pbdevname,pdata+1,100);
|
|
pbdevname[99] = 0;
|
|
memcpy(capdevname,pdata+1+100,100);
|
|
capdevname[99] = 0;
|
|
printf("get Audiodevs: <%s><%s>\n",pbdevname,capdevname);
|
|
newaudiodevs = 1;
|
|
}
|
|
|
|
if(pdata[0] == 8)
|
|
{
|
|
uint32_t rxbaseqrg;
|
|
rxbaseqrg = pdata[1];
|
|
rxbaseqrg <<= 8;
|
|
rxbaseqrg += pdata[2];
|
|
rxbaseqrg <<= 8;
|
|
rxbaseqrg += pdata[3];
|
|
rxbaseqrg <<= 8;
|
|
rxbaseqrg += pdata[4];
|
|
|
|
uint32_t txbaseqrg;
|
|
txbaseqrg = pdata[5];
|
|
txbaseqrg <<= 8;
|
|
txbaseqrg += pdata[6];
|
|
txbaseqrg <<= 8;
|
|
txbaseqrg += pdata[7];
|
|
txbaseqrg <<= 8;
|
|
txbaseqrg += pdata[8];
|
|
|
|
RX_FREQ = (double)rxbaseqrg;
|
|
TX_FREQ = (double)txbaseqrg;
|
|
|
|
resetqrgs = 1;
|
|
}
|
|
|
|
if(pdata[0] == 9)
|
|
{
|
|
compressor = pdata[1];
|
|
printf("compressor: %d\n",compressor);
|
|
}
|
|
|
|
if(pdata[0] == 10)
|
|
{
|
|
if(pdata[1] == 1)
|
|
{
|
|
// Pluto on local USB
|
|
*plutoid = 0;
|
|
printf("Pluto on local USB\n");
|
|
}
|
|
else
|
|
{
|
|
// Pluto on ETH
|
|
memset(plutoid,0,sizeof(plutoid));
|
|
strcpy(plutoid,"ip:");
|
|
memcpy(plutoid+3,pdata+2,len-2);
|
|
printf("Pluto on Ethernet IP: <%s>\n",plutoid);
|
|
}
|
|
gotPlutoID = 1;
|
|
}
|
|
|
|
if(pdata[0] == 11)
|
|
{
|
|
audiohighpass = pdata[1];
|
|
micboost = pdata[2];
|
|
//showbytestring("cmd11:",pdata,3,3);
|
|
}
|
|
|
|
if(pdata[0] == 12)
|
|
rxfilter = pdata[1];
|
|
|
|
if(pdata[0] == 13)
|
|
txfilter = pdata[1];
|
|
|
|
if(pdata[0] == 14)
|
|
rxmute = pdata[1];
|
|
|
|
if(pdata[0] == 15)
|
|
{
|
|
int val;
|
|
val = pdata[1];
|
|
val <<= 8;
|
|
val += pdata[2];
|
|
val <<= 8;
|
|
val += pdata[3];
|
|
val <<= 8;
|
|
val += pdata[4];
|
|
|
|
val -= 12000;
|
|
refoffset = val;
|
|
printf("refoffset: %d\n",refoffset);
|
|
|
|
resetqrgs = 1;
|
|
}
|
|
|
|
if(pdata[0] == 16)
|
|
beaconlock = pdata[1];
|
|
|
|
if(pdata[0] == 17)
|
|
fftspeed = pdata[1];
|
|
|
|
if(pdata[0] == 19)
|
|
{
|
|
txpower = pdata[1];
|
|
txpower = -txpower; // convert received pos value into correct neg value
|
|
if(txpower > 0) txpower = 0;
|
|
if(txpower < -60) txpower = -60;
|
|
}
|
|
|
|
if(pdata[0] == 20)
|
|
recpb = pdata[1];
|
|
|
|
if(pdata[0] == 21)
|
|
{
|
|
int r = system("shutdown now");
|
|
exit(r);
|
|
}
|
|
|
|
if(pdata[0] == 22)
|
|
{
|
|
// send test tone
|
|
setSendtone(1-sendtone);
|
|
}
|
|
|
|
if(pdata[0] == 23)
|
|
{
|
|
pttmode = pdata[1];
|
|
}
|
|
}
|
|
|
|
void close_program()
|
|
{
|
|
printf("\nCtrl-C pressed\n");
|
|
keeprunning = 0;
|
|
sleep(1);
|
|
close_liquid();
|
|
close_liquid_modulator();
|
|
close_fft();
|
|
close_gpio();
|
|
}
|
|
|
|
int main ()
|
|
{
|
|
printf("=== QO100 Transceiver Pluto-Driver, by DJ0ABR ===\n");
|
|
|
|
// linux error number see:
|
|
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h
|
|
/*char s1[100];
|
|
iio_strerror(-19, s1, 99);
|
|
printf("<%s>\n",s1);
|
|
exit(0);*/
|
|
|
|
char url[512];
|
|
|
|
// check if we are connected to a router
|
|
sprintf(url,"ip route");
|
|
char *pr = runProgram(url, sizeof(url)-1);
|
|
//printf("<%s>\n",pr);
|
|
if(strstr(pr,"default via"))
|
|
{
|
|
// we are on a router, check github for updates
|
|
sprintf(url,"wget --no-check-certificate --no-cache --no-cookies --no-http-keep-alive -O version.txt https://raw.githubusercontent.com/dj0abr/QO100_Transceiver/main/version.txt?cachekiller=%d",rand());
|
|
int sres = system(url);
|
|
if(sres < 0)
|
|
{
|
|
printf("error %d when reading actual serial number\n",sres);
|
|
}
|
|
}
|
|
|
|
install_signal_handler(close_program);
|
|
|
|
// Install FIFOs
|
|
RXfifo = create_fifo(200, PLUTOBUFSIZE*4);
|
|
TXfifo = create_fifo(200, PLUTOBUFSIZE*4);
|
|
FFTfifo = create_fifo(200, PLUTOBUFSIZE*4);
|
|
CWfifo = create_fifo(10,48*4);
|
|
|
|
// start audio (soundcard) and get connected devices
|
|
if(kmaudio_init() == -1)
|
|
{
|
|
printf("NO AUDIO device\n");
|
|
exit(0);
|
|
}
|
|
kmaudio_getDeviceList();
|
|
|
|
// UDP receiver for commands from GUI
|
|
UdpRxInit(&udprxsock, 40821, udprxfunc , &keeprunning);
|
|
|
|
// send audio devices to GUI
|
|
int len;
|
|
uint8_t *s = io_getAudioDevicelist(&len);
|
|
uint8_t ub[len+1+2];
|
|
ub[0] = 4; // ID for sound device string
|
|
ub[1] = ((uint16_t)DRIVER_SERIAL) >> 8; // driver serial number
|
|
ub[2] = ((uint16_t)DRIVER_SERIAL) & 0xff;
|
|
memcpy(ub+3,s,len);
|
|
sendUDP(gui_ip, GUI_UDPPORT, ub, len+1+2);
|
|
|
|
// wait for initial configuration from GUI
|
|
// the GUI sends now:
|
|
// selected Audio Device (udp code 7)
|
|
// Base QRGs (udp code 8)
|
|
// Pluto Address (udp code 10)
|
|
int to=0;
|
|
while(gotPlutoID == 0)
|
|
{
|
|
usleep(100000);
|
|
if(++to >= 50)
|
|
{
|
|
printf("no config from GUI, exit program\n");
|
|
exit(0);
|
|
}
|
|
}
|
|
printf("initial config OK, continue\n");
|
|
|
|
// find plutoid
|
|
if(*plutoid == 'i')
|
|
{
|
|
strcpy(pluto_context_name,plutoid);
|
|
}
|
|
else
|
|
{
|
|
// automatically search a pluto connected via USB
|
|
int res = pluto_get_USB(plutoid);
|
|
if(!res)
|
|
{
|
|
printf("no Pluto found on USB, exit program\n");
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
// Pluto found, now initialize it
|
|
printf("Pluto IP/USB ID : <%s>\n",pluto_context_name);
|
|
pluto_setup();
|
|
|
|
// init FFT
|
|
init_fft();
|
|
|
|
// init DSP demodulator
|
|
init_liquid();
|
|
|
|
// init DSP modulator
|
|
init_liquid_modulator();
|
|
|
|
// pluto RX/TX is now running, samples are available in the fifos
|
|
// start the SSB receiver and transmitter
|
|
init_rx();
|
|
init_tx();
|
|
|
|
// init rotary encoders on RPI
|
|
init_rotencoder();
|
|
|
|
release_ptt();
|
|
|
|
printf("initialisation finished. Enter normal operation (press Ctrl+C to cancel)\n*** main-PID:%ld ***\n",syscall(SYS_gettid));
|
|
while(keeprunning)
|
|
{
|
|
// main loop
|
|
// time-uncritical jobs are done here
|
|
|
|
if(newaudiodevs)
|
|
{
|
|
if(*pbdevname && *capdevname)
|
|
{
|
|
// audio device names available
|
|
printf("close streams\n");
|
|
if(pbidx!=-1) close_stream(pbidx);
|
|
if(capidx!=-1) close_stream(capidx);
|
|
printf("init streams\n");
|
|
|
|
pbidx = kmaudio_startPlayback(pbdevname, AUDIOSAMPRATE);
|
|
if(pbidx == -1)
|
|
printf("NO AUDIO play device: <%s>\n",pbdevname);
|
|
|
|
capidx = kmaudio_startCapture(capdevname, AUDIOSAMPRATE);
|
|
if(capidx == -1)
|
|
printf("NO AUDIO record device: <%s>\n",capdevname);
|
|
|
|
newaudiodevs = 0;
|
|
}
|
|
}
|
|
|
|
if(resetqrgs)
|
|
{
|
|
resetqrgs = 0;
|
|
|
|
// set pluto tuner frequency if changed by user
|
|
setRXfrequency((long long)RX_FREQ);
|
|
setTXfrequency((long long)TX_FREQ);
|
|
}
|
|
|
|
setTXpower();
|
|
|
|
static int ms10 = 0;
|
|
if(++ms10 >= 100)
|
|
{
|
|
ms10 = 0;
|
|
int rotfreq = getEncSteps(0);
|
|
if(rotfreq != 0)
|
|
{
|
|
uint8_t rxoff[2];
|
|
rxoff[0] = 7;
|
|
rxoff[1] = (uint8_t)(rotfreq+128);
|
|
sendUDP(gui_ip, GUI_UDPPORT, rxoff, 2);
|
|
}
|
|
}
|
|
|
|
int rotvol = getEncSteps(1);
|
|
if(rotvol != 0)
|
|
{
|
|
rotvol = -rotvol;
|
|
|
|
rxvolume += (float)rotvol / 50.0f;
|
|
if(rxvolume < 0.02f) rxvolume = 0.02f;
|
|
// max volume is limited just before sending to sound driver
|
|
}
|
|
|
|
int nptt = getPort("p");
|
|
if(nptt == 1) extpttinput_enabled = 1;
|
|
if(extpttinput_enabled)
|
|
{
|
|
nptt = test_ptt_gpio();
|
|
if(pttmode == 1)
|
|
{
|
|
if(nptt == 3) ptt = 1;
|
|
if(nptt == 2) ptt = 0;
|
|
}
|
|
else
|
|
if(nptt == 3) ptt = 1-ptt;
|
|
}
|
|
|
|
int nmute = getPort("m");
|
|
if(nmute == 1) mute_enabled = 1;
|
|
if(mute_enabled)
|
|
{
|
|
nmute = test_mute_gpio();
|
|
if(nmute == 3)
|
|
{
|
|
if(mute) mute = 0;
|
|
else mute = 1;
|
|
}
|
|
}
|
|
|
|
// switch PTT output according to variable "ptt"
|
|
if(ptt != lastptt)
|
|
{
|
|
if(ptt)
|
|
{
|
|
// switch to TX mode
|
|
setSendtone(0);// never start with a test tone after pressing PTT
|
|
io_fifo_clear(capidx);
|
|
fifo_clear(TXfifo);
|
|
set_ptt();
|
|
}
|
|
|
|
if(!ptt) release_ptt(); // switch to RX mode
|
|
|
|
lastptt = ptt;
|
|
}
|
|
|
|
//printf("%d\n",getPort("c"));
|
|
|
|
usleep(1000); // do NOT change, used for some simple timings
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void setSendtone(int onoff)
|
|
{
|
|
sendtone = onoff;
|
|
|
|
uint8_t st[2];
|
|
st[0] = 10;
|
|
st[1] = (uint8_t)sendtone;
|
|
sendUDP(gui_ip, GUI_UDPPORT, st, 2);
|
|
}
|