-//#define MICSAMPLES // define to send microphone samples (triggered by attenuator slider)
-//#define TX_ENVELOPE // define to dump TX envelope 1 sec after first PTT
/*
* HPSDR simulator, (C) Christoph van Wuellen, April/Mai 2019
*
* and the PURESIGNAL feedback signal is connected to the second ADC.
*
* Audio sent to the "radio" is played via the first available output channel.
- * This works on MacOS (PORTAUDIO) and Linux (ALSA).
+ * This works on MacOS (PORTAUDIO) and Linux (ALSASOUND).
*
* Additional feature include the recording of the TX envelope of the first second
* of TXing, and the possiblity to read a file with mic samples and "send"
#include <netinet/in.h>
#include <netinet/tcp.h>
+#define NEED_DUMMY_AUDIO 1
+
#ifdef PORTAUDIO
#include "portaudio.h"
-#else
+#undef NEED_DUMMY_AUDIO
+#endif
+#ifdef ALSASOUND
#include <alsa/asoundlib.h>
+#undef NEED_DUMMY_AUDIO
#endif
// Forward declarations for the audio functions
static int sock_TCP_Server = -1;
static int sock_TCP_Client = -1;
+//
+// Forward declarations for new protocol stuff
+//
+void new_protocol_general_packet(unsigned char *buffer);
+int new_protocol_running(void);
+
+#define LENNOISE 192000
+#define NOISEDIV (RAND_MAX / 96000)
+
+double noiseItab[LENNOISE];
+double noiseQtab[LENNOISE];
+
/*
* These variables store the state of the SDR.
* Whenevery they are changed, this is reported.
static double txatt_dbl = 1.0;
static double rxatt_dbl[4] = {1.0, 1.0, 1.0, 1.0}; // this reflects both ATT and PREAMP
-#ifdef TX_ENVELOPE
-//
-// This records the size of the first MAXENV TX samples after the first RX-TX transition.
-// This can be used to detect whether RX/TX transitions are fast enough not to chop
-// the first part of an SSB transmission using VOX, or of the first CW element.
-//
-#define MAXENV 48000
-static float envelope[MAXENV];
-int envptr=0;
-#endif
-
-#ifdef MICSAMPLES
-//
-// Raw audio data (48000 16-bit int words) is read from a file and stored.
-// As soon as the attenuation of RX1 goes above 20dB, these samples are
-// sent to the SDR (one shot), and when the attenuation goes below 10dB, this system
-// is reset, so playing with the ALEX ATT buttons or with the ATT slider you can
-// send mic samples to the SDR application and do some testing.
-//
-static int16_t micsamples[48000];
-int micsamples_ptr=-2;
-int micsamples_rate=0;
-#endif
-
-static int sock_ep2;
-
-static struct sockaddr_in addr_ep6;
+/*
+ * Socket for communicating with the "PC side"
+ */
+static int sock_udp;
+struct sockaddr_in addr_new; // shared by newhpsdrsim.c
+static struct sockaddr_in addr_old;
/*
* These two variables monitor whether the TX thread is active
// and two METIS packets per TCP/UDP packet,
// and two/four/eight-fold up-sampling if the TX sample
// rate is 96000/192000/384000
+//
+// In the new protocol, TX samples come in bunches of
+// 240 samples. So NEWRTXLEN is defined as a multiple of
+// 240 not exceeding RTXLEN
+//
#define RTXLEN 64512
-static double isample[RTXLEN];
-static double qsample[RTXLEN];
+#define NEWRTXLEN 64320
+double isample[RTXLEN]; // shared with newhpsdrsim
+double qsample[RTXLEN]; // shared with newhpsdrsim
static double last_i_sample=0.0;
static double last_q_sample=0.0;
static int txptr=0;
static int rxptr=0;
-static int ismetis,isorion,ishermes,isc25;
+
+#define DEVICE_ATLAS 0
+#define DEVICE_HERMES 1
+#define DEVICE_HERMES2 2
+#define DEVICE_ANGELIA 3
+#define DEVICE_ORION 4
+#define DEVICE_ORION2 5
+#define DEVICE_HERMESLITE 6
+#define DEVICE_C25 100
+static int DEVICE=DEVICE_HERMES;
int main(int argc, char *argv[])
{
- int j, size;
+ int i, j, size;
struct sched_param param;
pthread_attr_t attr;
pthread_t thread;
- int DEVICE;
uint8_t reply[11] = { 0xef, 0xfe, 2, 0, 0, 0, 0, 0, 0, 32, 1 };
uint32_t code;
int16_t sample,l,r;
- struct sockaddr_in addr_ep2, addr_from;
+ struct sockaddr_in addr_udp;
uint8_t buffer[1032];
- struct iovec iovec;
- struct msghdr msghdr;
struct timeval tv;
struct timespec ts;
int yes = 1;
uint8_t *bp;
+ unsigned long checksum;
+ socklen_t lenaddr;
+ struct sockaddr_in addr_from;
uint32_t last_seqnum = 0xffffffff, seqnum; // sequence number of received packet
int bytes_read, bytes_left;
uint32_t *code0 = (uint32_t *) buffer; // fast access to code of first buffer
int fd;
+ long cnt;
+
+ // Produce some noise
+ j=RAND_MAX / 2;
+ for (i=0; i<LENNOISE; i++) {
+ noiseItab[i]= ((double) rand() / j - 1.0) * 0.00003;
+ noiseQtab[i]= ((double) rand() / j - 1.0) * 0.00003;
+ }
+
+ memset (isample, 0, RTXLEN*sizeof(double));
+ memset (qsample, 0, RTXLEN*sizeof(double));
-#ifdef MICSAMPLES
- fd=open("micsamples", O_RDONLY);
- if (fd >= 0) {
- if (read(fd, micsamples, 48000*sizeof(int16_t)) == 48000*sizeof(int16_t)) {
- fprintf(stderr,"MIC SAMPLES available\n");
- micsamples_ptr=-1;
- }
- }
-#endif
-
audio_get_cards();
audio_open_output();
/*
* Examples for ORION2: ANAN7000D, ANAN8000D
*/
- DEVICE=1; // default is Hermes
if (argc > 1) {
- if (!strncmp(argv[1],"-metis" ,6)) DEVICE=0;
- if (!strncmp(argv[1],"-hermes" ,7)) DEVICE=1;
- if (!strncmp(argv[1],"-orion" , 6)) DEVICE=5;
- if (!strncmp(argv[1],"-orion2" ,7)) DEVICE=10; // Anan7000 in old protocol
- if (!strncmp(argv[1],"-c25" ,8)) DEVICE=100; // the same as hermes
+ if (!strncmp(argv[1],"-atlas" , 6)) DEVICE=DEVICE_ATLAS;
+ if (!strncmp(argv[1],"-hermes" , 7)) DEVICE=DEVICE_HERMES;
+ if (!strncmp(argv[1],"-hermes2" , 8)) DEVICE=DEVICE_HERMES2;
+ if (!strncmp(argv[1],"-angelia" , 8)) DEVICE=DEVICE_ANGELIA;
+ if (!strncmp(argv[1],"-orion" , 6)) DEVICE=DEVICE_ORION;
+ if (!strncmp(argv[1],"-orion2" , 7)) DEVICE=DEVICE_ORION2;
+ if (!strncmp(argv[1],"-hermeslite" , 11)) DEVICE=DEVICE_ORION2;
+ if (!strncmp(argv[1],"-c25" , 4)) DEVICE=DEVICE_C25;
}
- ismetis=ishermes=isorion=isc25=0;
switch (DEVICE) {
- case 0: fprintf(stderr,"DEVICE is METIS\n"); ismetis=1; break;
- case 1: fprintf(stderr,"DEVICE is HERMES\n"); ishermes=1; break;
- case 5: fprintf(stderr,"DEVICE is ORION\n"); isorion=1; break;
- case 10: fprintf(stderr,"DEVICE is ORION2\n"); isorion=1; break;
- case 100: fprintf(stderr,"DEVICE is StemLab\n"); ishermes=1; isc25=1; break;
+ case DEVICE_ATLAS: fprintf(stderr,"DEVICE is ATLASS\n"); break;
+ case DEVICE_HERMES: fprintf(stderr,"DEVICE is HERMES\n"); break;
+ case DEVICE_HERMES2: fprintf(stderr,"DEVICE is HERMES (2)\n"); break;
+ case DEVICE_ANGELIA: fprintf(stderr,"DEVICE is ANGELIA\n"); break;
+ case DEVICE_ORION: fprintf(stderr,"DEVICE is ORION\n"); break;
+ case DEVICE_ORION2: fprintf(stderr,"DEVICE is ORION-II\n"); break;
+ case DEVICE_C25: fprintf(stderr,"DEVICE is STEMlab/C25\n"); break;
}
reply[10]=DEVICE;
- if ((sock_ep2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ if ((sock_udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket");
return EXIT_FAILURE;
reply[7]=0xEE;
reply[8]=0xFF;
- setsockopt(sock_ep2, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+ setsockopt(sock_udp, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
tv.tv_sec = 0;
tv.tv_usec = 1000;
- setsockopt(sock_ep2, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+ setsockopt(sock_udp, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
- memset(&addr_ep2, 0, sizeof(addr_ep2));
- addr_ep2.sin_family = AF_INET;
- addr_ep2.sin_addr.s_addr = htonl(INADDR_ANY);
- addr_ep2.sin_port = htons(1024);
+ memset(&addr_udp, 0, sizeof(addr_udp));
+ addr_udp.sin_family = AF_INET;
+ addr_udp.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr_udp.sin_port = htons(1024);
- if (bind(sock_ep2, (struct sockaddr *)&addr_ep2, sizeof(addr_ep2)) < 0)
+ if (bind(sock_udp, (struct sockaddr *)&addr_udp, sizeof(addr_udp)) < 0)
{
perror("bind");
return EXIT_FAILURE;
tv.tv_usec = 1000;
setsockopt(sock_TCP_Server, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
- if (bind(sock_TCP_Server, (struct sockaddr *)&addr_ep2, sizeof(addr_ep2)) < 0)
+ if (bind(sock_TCP_Server, (struct sockaddr *)&addr_udp, sizeof(addr_udp)) < 0)
{
perror("bind tcp");
return EXIT_FAILURE;
while (1)
{
- memset(&iovec, 0, sizeof(iovec));
- memset(&msghdr, 0, sizeof(msghdr));
-
memcpy(buffer, id, 4);
- iovec.iov_base = buffer;
- iovec.iov_len = 1032;
- msghdr.msg_iov = &iovec;
- msghdr.msg_iovlen = 1;
- msghdr.msg_name = &addr_from;
- msghdr.msg_namelen = sizeof(addr_from);
ts.tv_sec = 0;
ts.tv_nsec = 1000000;
}
else
{
- bytes_read = recvmsg(sock_ep2, &msghdr, 0);
+ lenaddr=sizeof(addr_from);
+ bytes_read = recvfrom(sock_udp, buffer, 1032, 0, (struct sockaddr *)&addr_from, &lenaddr);
if (bytes_read > 0)
{
udp_retries=0;
switch (code)
{
- // PC to Red Pitaya transmission via process_ep2
- case 0x0201feef:
+ // PC to Red Pitaya transmission via process_ep2
+ case 0x0201feef:
// processing an invalid packet is too dangerous -- skip it!
if (bytes_read != 1032)
// Note that this interpolation causes weak "sidebands" at 48/96/... kHz distance (the
// strongest ones at 48 kHz).
double disample,dqsample,idelta,qdelta;
+ double sum;
bp=buffer+16; // skip 8 header and 8 SYNC/C&C bytes
+ sum=0.0;
for (j=0; j<126; j++) {
// write audio samples
r = (int)((signed char) *bp++)<<8;
audio_write(r,l);
sample = (int)((signed char) *bp++)<<8;
sample |= (int) ((signed char) *bp++ & 0xFF);
- disample=(double) sample / 32768.0;
+ disample=(double) sample * 0.000030517578125; // division by 32768
sample = (int)((signed char) *bp++)<<8;
sample |= (int) ((signed char) *bp++ & 0xFF);
- dqsample=(double) sample / 32768.0;
-
-#ifdef TX_ENVELOPE
- if (ptt || envptr > 0) {
- if (envptr < MAXENV) envelope[envptr++]=(disample*disample+dqsample*dqsample);
- if (envptr == MAXENV) {
- fd=open("dump.envelope", O_RDWR | O_CREAT | O_TRUNC, (mode_t) 0777);
- if (fd >= 0) {
- write (fd, envelope, MAXENV*sizeof(float));
- close (fd);
- fprintf(stderr,"TX ENV dumped\n");
- }
- envptr++;
- }
- }
-#endif
+ dqsample=(double) sample * 0.000030517578125;
+ sum += (disample*disample+dqsample*dqsample);
+
switch (rate) {
case 0: // RX sample rate = TX sample rate = 48000
isample[txptr ]=disample;
last_q_sample=dqsample;
if (j == 62) bp+=8; // skip 8 SYNC/C&C bytes of second block
}
+ fprintf(stderr,"LEVEL=%f\n", sum*0.007936508);
// wrap-around of ring buffer
if (txptr >= RTXLEN) txptr=0;
}
break;
- // respond to an incoming Metis detection request
- case 0x0002feef:
+ // respond to an incoming Metis detection request
+ case 0x0002feef:
fprintf(stderr, "Respond to an incoming Metis detection request / code: 0x%08x\n", code);
}
else
{
- sendto(sock_ep2, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+ sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
}
break;
- // stop the Red Pitaya to PC transmission via handler_ep6
- case 0x0004feef:
+ // stop the Red Pitaya to PC transmission via handler_ep6
+ case 0x0004feef:
fprintf(stderr, "STOP the transmission via handler_ep6 / code: 0x%08x\n", code);
}
break;
- // start the Red Pitaya to PC transmission via handler_ep6
- case 0x1104feef:
- //// This special code 0x11 is no longer needed, is does exactly the same thing
- //// as the other start codes 0x01, 0x02, 0x03
+ // start the Red Pitaya to PC transmission via handler_ep6
+ case 0x1104feef:
fprintf(stderr, "TCP METIS-start message received / code: 0x%08x\n", code);
/* FALLTHROUGH */
- case 0x0104feef:
- case 0x0204feef:
- case 0x0304feef:
+ case 0x0104feef:
+ case 0x0204feef:
+ case 0x0304feef:
fprintf(stderr, "START the handler_ep6 thread / code: 0x%08x\n", code);
enable_thread = 0;
while (active_thread) usleep(1000);
- memset(&addr_ep6, 0, sizeof(addr_ep6));
- addr_ep6.sin_family = AF_INET;
- addr_ep6.sin_addr.s_addr = addr_from.sin_addr.s_addr;
- addr_ep6.sin_port = addr_from.sin_port;
+ memset(&addr_old, 0, sizeof(addr_old));
+ addr_old.sin_family = AF_INET;
+ addr_old.sin_addr.s_addr = addr_from.sin_addr.s_addr;
+ addr_old.sin_port = addr_from.sin_port;
//
// The initial value of txptr defines the delay between
CommonMercuryFreq = 0;
if (pthread_create(&thread, NULL, handler_ep6, NULL) < 0)
{
- perror("pthread_create");
+ perror("create old protocol thread");
return EXIT_FAILURE;
}
pthread_detach(thread);
break;
- default:
- // Possibly a discovery packet of the New protocol, otherwise a severe error
- if (bytes_read == 60 && code == 0 && buffer[4] == 0x02)
- {
- fprintf(stderr,"NewProtocol discovery packet received (we won't respond)\n");
+ default:
+ /*
+ * Here we end up with several possible packets from the new protocol
+ * These are processed here.
+ */
+ if (bytes_read == 264 && buffer[0] == 0xEF && buffer[1] == 0xFE && buffer[2] == 0x03 && buffer[3] == 0x01) {
+ static long cnt=0;
+ unsigned long blks=(buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7];
+ fprintf(stderr,"OldProtocol Program blks=%lu count=%ld\r", blks, ++cnt);
+ usleep(1000);
+ memset(buffer, 0, 60);
+ buffer[0]=0xEF;
+ buffer[1]=0xFE;
+ buffer[2]=0x04;
+ buffer[3]=0xAA;
+ buffer[4]=0xBB;
+ buffer[5]=0xCC;
+ buffer[6]=0xDD;
+ buffer[7]=0xEE;
+ buffer[8]=0xFF;
+ sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+ if (blks == cnt) fprintf(stderr,"\n\n Programming Done!\n");
+ break;
+ }
+ if (bytes_read == 64 && buffer[0] == 0xEF && buffer[1] == 0xFE && buffer[2] == 0x03 && buffer[3] == 0x02) {
+ fprintf(stderr,"OldProtocol Erase packet received:\n");
+ sleep(1);
+ cnt=0;
+ memset(buffer, 0, 60);
+ buffer[0]=0xEF;
+ buffer[1]=0xFE;
+ buffer[2]=0x03;
+ buffer[3]=0xAA;
+ buffer[4]=0xBB;
+ buffer[5]=0xCC;
+ buffer[6]=0xDD;
+ buffer[7]=0xEE;
+ buffer[8]=0xFF;
+ sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+ break;
+
+ }
+ if (bytes_read == 63 && buffer[0] == 0xEF && buffer[1] == 0xFE && buffer[2] == 0x03) {
+ fprintf(stderr,"OldProtocol SetIP packet received:\n");
+ fprintf(stderr,"MAC address is %02x:%02x:%02x:%02x:%02x:%02x\n", buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8]);
+ fprintf(stderr,"IP address is %03d:%03d:%03d:%03d\n", buffer[9], buffer[10], buffer[11], buffer[12]);
+ buffer[2]=0x02;
+ memset(buffer+9, 0, 54);
+ sendto(sock_udp, buffer, 63, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+ break;
+ }
+ if (code == 0 && buffer[4] == 0x02) {
+ fprintf(stderr,"NewProtocol discovery packet received\n");
+ // prepeare response
+ memset(buffer, 0, 60);
+ buffer [4]=0x02+new_protocol_running();
+ buffer [5]=0xAA;
+ buffer[ 6]=0xBB;
+ buffer[ 7]=0xCC;
+ buffer[ 8]=0xDD;
+ buffer[ 9]=0xEE;
+ buffer[10]=0xFF;
+ buffer[11]=DEVICE;
+ buffer[12]=38;
+ buffer[13]=103;
+ buffer[20]=2;
+ buffer[21]=1;
+ buffer[22]=3;
+ sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+ break;
+ }
+ if (code == 0 && buffer[4] == 0x04) {
+ fprintf(stderr,"NewProtocol erase packet received\n");
+ memset(buffer, 0, 60);
+ buffer [4]=0x02+active_thread;
+ buffer [5]=0xAA;
+ buffer[ 6]=0xBB;
+ buffer[ 7]=0xCC;
+ buffer[ 8]=0xDD;
+ buffer[ 9]=0xEE;
+ buffer[10]=0xFF;
+ buffer[11]=DEVICE;
+ buffer[12]=38;
+ buffer[13]=103;
+ buffer[20]=2;
+ buffer[21]=1;
+ buffer[22]=3;
+ sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+ sleep(5); // pretend erase takes 5 seconds
+ sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+ break;
+ }
+ if (bytes_read == 265 && buffer[4] == 0x05) {
+ unsigned long seq, blk;
+ seq=(buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+ blk=(buffer[5] << 24) + (buffer[6] << 16) + (buffer[7] << 8) + buffer[8];
+ fprintf(stderr,"NewProtocol Program packet received: seq=%lu blk=%lu\r",seq,blk);
+ if (seq == 0) checksum=0;
+ for (j=9; j<=264; j++) checksum += buffer[j];
+ memset(buffer+4, 0, 56); // keep seq. no
+ buffer[4]=0x04;
+ buffer [5]=0xAA;
+ buffer[ 6]=0xBB;
+ buffer[ 7]=0xCC;
+ buffer[ 8]=0xDD;
+ buffer[ 9]=0xEE;
+ buffer[10]=0xFF;
+ buffer[11]=103;
+ buffer[12]=DEVICE;
+ buffer[13]=(checksum >> 8) & 0xFF;
+ buffer[14]=(checksum ) & 0xFF;
+ sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+ if (seq+1 == blk) fprintf(stderr,"\n\nProgramming Done!\n");
+ break;
+ }
+ if (bytes_read == 60 && code == 0 && buffer[4] == 0x06) {
+ fprintf(stderr,"NewProtocol SetIP packet received for MAC %2x:%2x:%2x:%2x%2x:%2x IP=%d:%d:%d:%d\n",
+ buffer[5],buffer[6],buffer[7],buffer[8],buffer[9],buffer[10],
+ buffer[11],buffer[12],buffer[13],buffer[14]);
+ // only respond if this is for OUR device
+ if (buffer[ 5] != 0xAA) break;
+ if (buffer[ 6] != 0xBB) break;
+ if (buffer[ 7] != 0xCC) break;
+ if (buffer[ 8] != 0xDD) break;
+ if (buffer[ 9] != 0xEE) break;
+ if (buffer[10] != 0xFF) break;
+ memset(buffer, 0, 60);
+ buffer [4]=0x02+active_thread;
+ buffer [5]=0xAA;
+ buffer[ 6]=0xBB;
+ buffer[ 7]=0xCC;
+ buffer[ 8]=0xDD;
+ buffer[ 9]=0xEE;
+ buffer[10]=0xFF;
+ buffer[11]=DEVICE;
+ buffer[12]=38;
+ buffer[13]=103;
+ buffer[20]=2;
+ buffer[21]=1;
+ buffer[22]=3;
+ sendto(sock_udp, buffer, 60, 0, (struct sockaddr *)&addr_from, sizeof(addr_from));
+ break;
+ }
+ if (bytes_read == 60 && buffer[4] == 0x00) {
+ // handle general packet
+ memset(&addr_new, 0, sizeof(addr_new));
+ addr_new.sin_family = AF_INET;
+ addr_new.sin_addr.s_addr = addr_from.sin_addr.s_addr;
+ addr_new.sin_port = addr_from.sin_port;
+ new_protocol_general_packet(buffer);
+ break;
}
else
{
- fprintf(stderr,"PackedInvalidCode: 0x%08x (Len=%d)\n", code, bytes_read);
+ fprintf(stderr,"Invalid packet (len=%d) detected: ",bytes_read);
+ for (i=0; i<16; i++) fprintf(stderr,"%02x ",buffer[i]);
+ fprintf(stderr,"\n");
}
break;
}
}
- close(sock_ep2);
+ close(sock_udp);
if (sock_TCP_Client > -1)
{
chk_data(((frame[4] >> 6) & 1), MicTS, "TimeStampMic");
chk_data(((frame[4] >> 7) & 1), CommonMercuryFreq,"Common Mercury Freq");
- if (isc25) {
+ if (DEVICE == DEVICE_C25) {
// Charly25: has two 18-dB preamps that are switched with "preamp" and "dither"
// and two attenuators encoded in Alex-ATT
// Both only applies to RX1!
chk_data(frame[3] & 0x80,alexTRdisable,"ALEX T/R disable");
chk_data(frame[4],alex_lpf,"ALEX LPF");
// reset TX level. Leve a little head-room for noise
- txdrv_dbl=(double) txdrive / 256.0;
+ txdrv_dbl=(double) txdrive * 0.00390625; // div. by. 256
break;
case 20:
chk_data((frame[4] & 0x1F) >> 0, rx_att[0], "RX1 ATT");
chk_data((frame[4] & 0x20) >> 5, rx1_attE, "RX1 ATT enable");
-#ifdef MICSAMPLES
- if (rx_att[0] > 20 && micsamples_ptr == -1) micsamples_ptr = 0;
- if (rx_att[0] < 10 && micsamples_ptr == 48000) micsamples_ptr = -1;
-#endif
- if (!isc25) {
+ if (DEVICE != DEVICE_C25) {
// Set RX amplification factors. No switchable preamps available normally.
rxatt_dbl[0]=pow(10.0, -0.05*(10*AlexAtt+rx_att[0]));
rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1]));
chk_data((frame[2] & 0x30) >> 4, rx_adc[6], "RX7 ADC");
chk_data((frame[3] & 0x1f), txatt, "TX ATT");
txatt_dbl=pow(10.0, -0.05*(double) txatt);
- if (isc25) {
+ if (DEVICE == DEVICE_C25) {
// RedPitaya: Hard-wired ADC settings.
rx_adc[0]=0;
rx_adc[1]=1;
}
}
-//
-// The "RX" signal. This is some noise, and some frequencies
-//
-#define LENNOISE 48000
-#define NOISEDIV (RAND_MAX / 24000)
-
-static double noiseItab[LENNOISE];
-static double noiseQtab[LENNOISE];
-
static double T0800Itab[480];
static double T0800Qtab[480];
static double T2000Itab[192];
int i, j, k, n, size;
int data_offset, header_offset;
uint32_t counter;
- uint16_t audio[512];
uint8_t buffer[1032];
uint8_t *pointer;
- struct iovec iovec;
- struct msghdr msghdr;
uint8_t id[4] = { 0xef, 0xfe, 1, 6 };
uint8_t header[40] =
{
double run,inc;
double i1,q1,fac;
- memset(audio, 0, sizeof(audio));
- memset(&iovec, 0, sizeof(iovec));
- memset(&msghdr, 0, sizeof(msghdr));
-
- memcpy(buffer + i * 1032, id, 4);
- iovec.iov_base = buffer;
- iovec.iov_len = 1032;
- msghdr.msg_iov = &iovec;
- msghdr.msg_iovlen = 1;
- msghdr.msg_name = &addr_ep6;
- msghdr.msg_namelen = sizeof(addr_ep6);
+ memcpy(buffer, id, 4);
header_offset = 0;
counter = 0;
//
// Produce RX data
//
- // a) noise from a 16-bit ADC
- //
- k=RAND_MAX / 2;
- for (i=0; i<LENNOISE; i++) {
- noiseItab[i]= ((double) rand() / k - 1.0) * 0.00003;
- noiseQtab[i]= ((double) rand() / k - 1.0) * 0.00003;
- }
noiseIQpt=0;
//
// b) some tones in the upper side band (one wave)
memset(pointer, 0, 504);
for (j=0; j<n; j++) {
// ADC1: noise + weak tone on RX, feedback sig. on TX (except STEMlab)
- if (ptt && !isc25) {
+ if (ptt && (DEVICE != DEVICE_C25)) {
i1=isample[rxptr]*txdrv_dbl;
q1=qsample[rxptr]*txdrv_dbl;
fac=IM3a+IM3b*(i1*i1+q1*q1);
adc1qsample += T0800Qtab[pt0800] * 83.886070 *rxatt_dbl[0];
}
// ADC2: noise + stronger tone on RX, feedback sig. on TX (only STEMlab)
- if (ptt && isc25) {
+ if (ptt && (DEVICE == DEVICE_C25)) {
i1=isample[rxptr]*txdrv_dbl;
q1=qsample[rxptr]*txdrv_dbl;
fac=IM3a+IM3b*(i1*i1+q1*q1);
myqsample=0;
break;
}
- if (ismetis && ptt && (k==1)) {
+ if (DEVICE == DEVICE_ATLAS && ptt && (k==1)) {
// METIS: TX DAC signal goes to RX2 when TXing
myisample=dacisample;
myqsample=dacqsample;
}
- if (ishermes && ptt && (k==3)) {
+ if ((DEVICE==DEVICE_HERMES || DEVICE==DEVICE_HERMES2 || DEVICE==DEVICE_C25) && ptt && (k==3)) {
// HERMES: TX DAC signal goes to RX4 when TXing
myisample=dacisample;
myqsample=dacqsample;
}
- if (isorion && ptt && (k==4)) {
+ if ((DEVICE==DEVICE_ANGELIA || DEVICE == DEVICE_ORION || DEVICE== DEVICE_ORION2) && ptt && (k==4)) {
// ANGELIA and beyond: TX DAC signal goes to RX5 when TXing
myisample=dacisample;
myqsample=dacqsample;
*pointer++ = (myqsample >> 8) & 0xFF;
*pointer++ = (myqsample >> 0) & 0xFF;
}
-#ifdef MICSAMPLES
- if (micsamples_ptr >= 0 && micsamples_ptr < 48000) {
- ssample=micsamples[micsamples_ptr];
- *pointer++ = (ssample >> 8) & 0xFF;
- *pointer++ = (ssample ) & 0xFF;
- micsamples_rate++;
- if (micsamples_rate == 1 << rate) {
- micsamples_rate=0;
- micsamples_ptr++;
- }
- } else {
- *pointer++ = 0;
- *pointer++ = 0;
- }
-#else
// Microphone samples: silence
pointer += 2;
-#endif
rxptr++; if (rxptr >= RTXLEN) rxptr=0;
noiseIQpt++; if (noiseIQpt == LENNOISE) noiseIQpt=rand() / NOISEDIV;
pt2000++; if (pt2000 == len2000) pt2000=0;
if (sock_TCP_Client > -1)
{
- if (sendmsg(sock_TCP_Client, &msghdr, 0) < 0)
+ if (sendto(sock_TCP_Client, buffer, 1032, 0, (struct sockaddr *)&addr_old, sizeof(addr_old)) < 0)
{
fprintf(stderr, "TCP sendmsg error occurred at sequence number: %u !\n", counter);
}
}
else
{
- sendmsg(sock_ep2, &msghdr, 0);
+ sendto(sock_udp, buffer, 1032, 0, (struct sockaddr *)&addr_old, sizeof(addr_old));
}
}
}
}
}
-
-#else
+#endif
+#ifdef ALSASOUND
//
// Audio functions based on LINUX ALSA
//
}
}
#endif
+
+//
+// Dummy audio functions if this is compiled without audio support
+//
+#ifdef NEED_DUMMY_AUDIO
+void audio_get_cards()
+{
+}
+void audio_open_output()
+{
+}
+void audio_write (int16_t l, int16_t r)
+{
+}
+#endif
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <math.h>
+
+extern struct sockaddr_in addr_new;
+extern void audio_write(int16_t r, int16_t l);
+
+#define NUMRECEIVERS 8
+
+#define LENNOISE 192000
+#define NOISEDIV (RAND_MAX / 96000)
+
+extern double noiseItab[LENNOISE];
+extern double noiseQtab[LENNOISE];
+
+#define IM3a 0.60
+#define IM3b 0.20
+
+#define RTXLEN 64512
+#define NEWRTXLEN 64320
+extern double isample[RTXLEN]; // shared with newhpsdrsim
+extern double qsample[RTXLEN]; // shared with newhpsdrsim
+static int txptr = 10000;
+
+/*
+ * These variables represent the state of the machine
+ */
+// data from general packet
+static int ddc_port = 0;
+static int duc_port = 0;
+static int hp_port = 0;
+static int shp_port = 0;
+static int audio_port = 0;
+static int duc0_port = 0;
+static int ddc0_port = 0;
+static int mic_port = 0;
+static int wide_port = 0;
+static int wide_enable = 0;
+static int wide_len = 0;
+static int wide_size = 0;
+static int wide_rate = 0;
+static int wide_ppf = 0;
+static int port_mm = 0;
+static int port_smm = 0;
+static int pwm_min = 0;
+static int pwm_max = 0;
+static int bits = 0;
+static int hwtim = 0;
+static int pa_enable = 0;
+static int alex0_enable = 0;
+static int alex1_enable = 0;
+static int mm_port = 0;
+static int smm_port = 0;
+static int iqform = 0;
+
+// data from rx specific packet
+static int adc=0;
+static int adcdither[8];
+static int adcrandom[8];
+static int ddcenable[NUMRECEIVERS];
+static int adcmap[NUMRECEIVERS];
+static int rxrate[NUMRECEIVERS];
+static int syncddc[NUMRECEIVERS];
+
+//data from tx specific packet
+static int dac=0;
+static int cwmode=0;
+static int sidelevel=0;
+static int sidefreq=0;
+static int speed=0;
+static int weight=0;
+static int hang=0;
+static int delay=0;
+static int txrate=0;
+static int ducbits=0;
+static int orion=0;
+static int gain=0;
+static int txatt=0;
+
+//stat from high-priority packet
+static int run=0;
+static int ptt[4];
+static int cwx=0;
+static int dot=0;
+static int dash=0;
+static unsigned long rxfreq[NUMRECEIVERS];
+static unsigned long txfreq=0;
+static int txdrive=0;
+static int w1400=0; // Xvtr and Audio enable
+static int ocout=0;
+static int db9=0;
+static int mercury_atts=0;
+static int alex0[32];
+static int alex1[32];
+static int stepatt0=0;
+static int stepatt1=0;
+
+//
+// floating point representation of TX-Drive and ADC0-Attenuator
+//
+static double rxatt0_dbl=1.0;
+static double rxatt1_dbl=1.0;
+static double txatt_dbl=1.0;
+static double txdrv_dbl = 0.0;
+
+// End of state variables
+
+static pthread_t ddc_specific_thread_id;
+static pthread_t duc_specific_thread_id;
+static pthread_t rx_thread_id[NUMRECEIVERS];
+static pthread_t tx_thread_id;
+static pthread_t mic_thread_id;
+static pthread_t audio_thread_id;
+static pthread_t highprio_thread_id = 0;
+static pthread_t send_highprio_thread_id;
+
+void *ddc_specific_thread(void*);
+void *duc_specific_thread(void*);
+void *highprio_thread(void*);
+void *send_highprio_thread(void*);
+void *rx_thread(void *);
+void *tx_thread(void *);
+void *mic_thread(void *);
+void *audio_thread(void *);
+
+static double txlevel;
+
+int new_protocol_running() {
+ if (run) return 1; else return 0;
+}
+
+void new_protocol_general_packet(unsigned char *buffer) {
+ static unsigned long seqnum=0;
+ unsigned long seqold;
+ int rc;
+
+ seqold = seqnum;
+ seqnum = (buffer[0] >> 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+ if (seqnum != 0 && seqnum != seqold+1 ) {
+ fprintf(stderr,"GP: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+ }
+
+ rc=(buffer[5] << 8) + buffer[6];
+ if (rc == 0) rc=1025;
+ if (rc != ddc_port) {
+ ddc_port=rc;
+ fprintf(stderr,"GP: RX specific rcv port is %4d\n", rc);
+ }
+ rc=(buffer[7] << 8) + buffer[8];
+ if (rc == 0) rc=1026;
+ if (rc != duc_port) {
+ duc_port=rc;
+ fprintf(stderr,"GP: TX specific rcv port is %4d\n", rc);
+ }
+ rc=(buffer[9] << 8) + buffer[10];
+ if (rc == 0) rc=1027;
+ if (rc != hp_port) {
+ hp_port=rc;
+ fprintf(stderr,"GP: HighPrio Port rcv port is %4d\n", rc);
+ }
+ rc=(buffer[11] << 8) + buffer[12];
+ if (rc == 0) rc=1025;
+ if (rc != shp_port) {
+ shp_port=rc;
+ fprintf(stderr,"GP: HighPrio Port snd port is %4d\n", rc);
+ }
+ rc=(buffer[13] << 8) + buffer[14];
+ if (rc == 0) rc=1028;
+ if (rc != audio_port) {
+ audio_port=rc;
+ fprintf(stderr,"GP: Audio rcv port is %4d\n", rc);
+ }
+ rc=(buffer[15] << 8) + buffer[16];
+ if (rc == 0) rc=1029;
+ if (rc != duc0_port) {
+ duc0_port=rc;
+ fprintf(stderr,"GP: TX data rcv base port is %4d\n", rc);
+ }
+ rc=(buffer[17] << 8) + buffer[18];
+ if (rc == 0) rc=1035;
+ if (rc != ddc0_port) {
+ ddc0_port=rc;
+ fprintf(stderr,"GP: RX data snd base port is %4d\n", rc);
+ }
+ rc=(buffer[19] << 8) + buffer[20];
+ if (rc == 0) rc=1026;
+ if (rc != mic_port) {
+ mic_port=rc;
+ fprintf(stderr,"GP: Microphone data snd port is %4d\n", rc);
+ }
+ rc=(buffer[21] << 8) + buffer[22];
+ if (rc == 0) rc=1027;
+ if (rc != wide_port) {
+ wide_port=rc;
+ fprintf(stderr,"GP: Wideband data snd port is %4d\n", rc);
+ }
+ rc=buffer[23];
+ if (rc != wide_enable) {
+ wide_enable = rc;
+ fprintf(stderr,"GP: Wideband Enable Flag is %d\n", rc);
+ }
+ rc=(buffer[24] << 8) + buffer[25]; if (rc == 0) rc=512;
+ if (rc != wide_len) {
+ wide_len=rc;
+ fprintf(stderr,"GP: WideBand Length is %d\n", rc);
+ }
+ rc=buffer[26]; if (rc == 0) rc=16;
+ if (rc != wide_size) {
+ wide_size=rc;
+ fprintf(stderr,"GP: Wideband sample size is %d\n", rc);
+ }
+ rc=buffer[27];
+ if (rc != wide_rate) {
+ wide_rate=rc;
+ fprintf(stderr,"GP: Wideband sample rate is %d\n", rc);
+ }
+ rc=buffer[28];
+ if (rc != wide_ppf) {
+ wide_ppf = rc;
+ fprintf(stderr,"GP: Wideband PPF is %d\n", rc);
+ }
+ rc = (buffer[29] << 8) + buffer[30];
+ if (rc != port_mm) {
+ port_mm=rc;
+ fprintf(stderr,"MemMapped Registers rcv port is %d\n", rc);
+ }
+ rc = (buffer[31] << 8) + buffer[32];
+ if (rc != port_smm) {
+ port_smm=rc;
+ fprintf(stderr,"MemMapped Registers snd port is %d\n", rc);
+ }
+ rc = (buffer[33] << 8) + buffer[34];
+ if (rc != pwm_min) {
+ pwm_min=rc;
+ fprintf(stderr,"GP: PWM Min value is %d\n", rc);
+ }
+ rc = (buffer[35] << 8) + buffer[36];
+ if (rc != pwm_max) {
+ pwm_max=rc;
+ fprintf(stderr,"GP: PWM Max value is %d\n", rc);
+ }
+ rc=buffer[37];
+ if (rc != bits) {
+ bits=rc;
+ fprintf(stderr,"GP: ModeBits=x%02x\n", rc);
+ }
+ rc=buffer[38];
+ if (rc != hwtim) {
+ hwtim=rc;
+ fprintf(stderr,"GP: Hardware Watchdog enabled=%d\n", rc);
+ }
+
+ iqform = buffer[39]; if (iqform == 0) iqform=3;
+ if (iqform != 3) fprintf(stderr,"GP: Wrong IQ Format requested: %d\n",iqform);
+
+ rc = (buffer[58] & 0x01);
+ if (rc != pa_enable) {
+ pa_enable=rc;
+ fprintf(stderr,"GP: PA enabled=%d\n", rc);
+ }
+
+ rc=buffer[59] & 0x01;
+ if (rc != alex0_enable) {
+ alex0_enable=rc;
+ fprintf(stderr,"GP: ALEX0 register enable=%d\n", rc);
+ }
+ rc=(buffer[59] & 0x02) >> 1;
+ if (rc != alex1_enable) {
+ alex1_enable=rc;
+ fprintf(stderr,"GP: ALEX1 register enable=%d\n", rc);
+ }
+ //
+ // Start HighPrio thread if we arrive here for the first time
+ // The HighPrio thread keeps running all the time.
+ //
+ if (!highprio_thread_id) {
+ if (pthread_create(&highprio_thread_id, NULL, highprio_thread, NULL) < 0) {
+ perror("***** ERROR: Create HighPrio thread");
+ }
+ pthread_detach(highprio_thread_id);
+
+ //
+ // init state arrays to zero for the first time
+ //
+ memset(adcdither, 0, 8*sizeof(int));
+ memset(adcrandom, 0, 8*sizeof(int));
+ memset(ddcenable, 0, NUMRECEIVERS*sizeof(int));
+ memset(adcmap, 0, NUMRECEIVERS*sizeof(int));
+ memset(syncddc, 0, NUMRECEIVERS*sizeof(int));
+
+ memset(rxfreq, 0, NUMRECEIVERS*sizeof(unsigned long));
+ memset(alex0, 0, 32*sizeof(int));
+ memset(alex1, 0, 32*sizeof(int));
+ memset(ptt , 0, 4*sizeof(int));
+ }
+}
+
+void *ddc_specific_thread(void *data) {
+ int sock;
+ struct sockaddr_in addr;
+ socklen_t lenaddr = sizeof(addr);
+ unsigned long seqnum,seqold;
+ struct timeval tv;
+ unsigned char buffer[2000];
+ int yes = 1;
+ int rc;
+ int i,j;
+
+ sock=socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("***** ERROR: RX specific: socket");
+ return NULL;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(ddc_port);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("***** ERROR: RX specific: bind");
+ return NULL;
+ }
+
+ while(run) {
+ rc = recvfrom(sock, buffer, 1444, 0,(struct sockaddr *)&addr, &lenaddr);
+ if (rc < 0 && errno != EAGAIN) {
+ perror("***** ERROR: DDC specific thread: recvmsg");
+ break;
+ }
+ if (rc < 0) continue;
+ if (rc != 1444) {
+ fprintf(stderr,"RX: Received DDC specific packet with incorrect length");
+ break;
+ }
+ seqold = seqnum;
+ seqnum = (buffer[0] >> 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+ if (seqnum != 0 &&seqnum != seqold+1 ) {
+ fprintf(stderr,"GP: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+ }
+ if (adc != buffer[4]) {
+ adc=buffer[4];
+ fprintf(stderr,"RX: Number of ADCs: %d\n",adc);
+ }
+ for (i=0; i<8; i++) {
+ rc=(buffer[5] >> i) & 0x01;
+ if (rc != adcdither[i]) {
+ adcdither[i]=rc;
+ fprintf(stderr,"RX: ADC%d dither=%d\n",i,rc);
+ }
+ }
+ for (i=0; i<8; i++) {
+ rc=(buffer[6] >> i) & 0x01;
+ if (rc != adcrandom[i]) {
+ adcrandom[i]=rc;
+ fprintf(stderr,"RX: ADC%d random=%d\n",i,rc);
+ }
+ }
+
+ for (i=0; i<NUMRECEIVERS; i++) {
+ int modified=0;
+
+ rc=(buffer[7 + (i/8)] >> (i % 8)) & 0x01;
+ if (rc != ddcenable[i]) {
+ modified=1;
+ ddcenable[i]=rc;
+ }
+
+ rc=buffer[17+6*i];
+ if (rc != adcmap[i]) {
+ modified=1;
+ adcmap[i]=rc;
+ }
+
+ rc=(buffer[18+6*i] << 8) + buffer[19+6*i];
+ if (rc != rxrate[i]) {
+ modified=1;
+ rxrate[i]=rc;
+ modified=1;
+ }
+
+ if (syncddc[i] != buffer[1363+i]) {
+ syncddc[i]=buffer[1363+i];
+ modified=1;
+ }
+ rc=(buffer[7 + (i/8)] >> (i % 8)) & 0x01;
+ if (rc != ddcenable[i]) {
+ modified=1;
+ ddcenable[i]=rc;
+ }
+ if (modified) {
+ fprintf(stderr,"RX: DDC%d Enable=%d ADC%d Rate=%d SyncMap=%02x\n",
+ i,ddcenable[i], adcmap[i], rxrate[i], syncddc[i]);
+ }
+ }
+ }
+ close(sock);
+ return NULL;
+}
+
+void *duc_specific_thread(void *data) {
+ int sock;
+ struct sockaddr_in addr;
+ socklen_t lenaddr=sizeof(addr);
+ unsigned long seqnum,seqold;
+ struct timeval tv;
+ unsigned char buffer[100];
+ int yes = 1;
+ int rc;
+
+ sock=socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("***** ERROR: TX specific: socket");
+ return NULL;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(duc_port);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("***** ERROR: TX specific: bind");
+ return NULL;
+ }
+
+ while(run) {
+ rc = recvfrom(sock, buffer, 60, 0,(struct sockaddr *)&addr, &lenaddr);
+ if (rc < 0 && errno != EAGAIN) {
+ perror("***** ERROR: DUC specific thread: recvmsg");
+ break;
+ }
+ if (rc < 0) continue;
+ if (rc != 60) {
+ fprintf(stderr,"TX: DUC Specific: wrong length\n");
+ break;
+ }
+ seqold = seqnum;
+ seqnum = (buffer[0] >> 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+ if (seqnum != 0 &&seqnum != seqold+1 ) {
+ fprintf(stderr,"GP: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+ }
+ if (dac != buffer[4]) {
+ dac=buffer[4];
+ fprintf(stderr,"TX: Number of DACs: %d\n", dac);
+ }
+ if (cwmode != buffer[5]) {
+ cwmode=buffer[5];
+ fprintf(stderr,"TX: CW mode bits = %x\n",cwmode);
+ }
+ if (sidelevel != buffer[6]) {
+ sidelevel=buffer[6];
+ fprintf(stderr,"TX: CW side tone level: %d\n", sidelevel);
+ }
+ rc=(buffer[7] << 8) + buffer[8];
+ if (rc != sidefreq) {
+ sidefreq = rc;
+ fprintf(stderr,"TX: CW sidetone freq: %d\n", sidefreq);
+ }
+ if (speed != buffer[9]) {
+ speed = buffer[9];
+ fprintf(stderr,"TX: CW keyer speed: %d wpm\n", speed);
+ }
+ if (weight != buffer[10]) {
+ weight=buffer[10];
+ fprintf(stderr,"TX: CW weight: %d\n", weight);
+ }
+ rc=(buffer[11] << 8) + buffer[12];
+ if (hang != rc) {
+ hang = rc;
+ fprintf(stderr,"TX: CW hang time: %d msec\n", hang);
+ }
+ if (delay != buffer[13]) {
+ delay=buffer[13];
+ fprintf(stderr,"TX: RF delay: %d msec\n", delay);
+ }
+ rc=(buffer[14] << 8) + buffer[15];
+ if (txrate != rc) {
+ txrate = rc;
+ fprintf(stderr,"TX: DUC sample rate: %d\n", rc);
+ }
+ if (ducbits != buffer[16]) {
+ ducbits=buffer[16];
+ fprintf(stderr,"TX: DUC sample width: %d bits\n", ducbits);
+ }
+ if (orion != buffer[50]) {
+ orion=buffer[50];
+ fprintf(stderr,"TX: ORION bits (mic etc): %x\n", orion);
+ }
+ if (gain != buffer[51]) {
+ gain= buffer[51];
+ fprintf(stderr,"TX: LineIn Gain (dB): %f\n", 12.0 - 1.5*gain);
+ }
+ if (txatt != buffer[59]) {
+ txatt = buffer[59];
+ txatt_dbl=pow(10.0, -0.05*txatt);
+ fprintf(stderr,"TX: ATT DUC0/ADC0: %d\n", txatt);
+ }
+ }
+ close(sock);
+ return NULL;
+}
+
+void *highprio_thread(void *data) {
+ int sock;
+ struct sockaddr_in addr;
+ socklen_t lenaddr=sizeof(addr);
+ unsigned long seqnum,seqold;
+ unsigned char buffer[2000];
+ struct timeval tv;
+ int yes = 1;
+ int rc;
+ unsigned long freq;
+ int i;
+
+ sock=socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("***** ERROR: HP: socket");
+ return NULL;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(hp_port);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("***** ERROR: HP: bind");
+ return NULL;
+ }
+
+ while(1) {
+ rc = recvfrom(sock, buffer, 1444, 0,(struct sockaddr *)&addr, &lenaddr);
+ if (rc < 0 && errno != EAGAIN) {
+ perror("***** ERROR: HighPrio thread: recvmsg");
+ break;
+ }
+ if (rc < 0) continue;
+ if (rc != 1444) {
+ fprintf(stderr,"Received HighPrio packet with incorrect length");
+ break;
+ }
+ seqold = seqnum;
+ seqnum = (buffer[0] >> 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+ if (seqnum != 0 &&seqnum != seqold+1 ) {
+ fprintf(stderr,"GP: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+ }
+ rc=(buffer[4] >> 0) & 0x01;
+ if (rc != run) {
+ run=rc;
+ fprintf(stderr,"HP: Run=%d\n", rc);
+ // if run=0, wait for threads to complete, otherwise spawn them off
+ if (run) {
+ if (pthread_create(&ddc_specific_thread_id, NULL, ddc_specific_thread, NULL) < 0) {
+ perror("***** ERROR: Create DDC specific thread");
+ }
+ if (pthread_create(&duc_specific_thread_id, NULL, duc_specific_thread, NULL) < 0) {
+ perror("***** ERROR: Create DUC specific thread");
+ }
+ for (i=0; i< NUMRECEIVERS; i++) {
+ if (pthread_create(&rx_thread_id[i], NULL, rx_thread, (void *) (uintptr_t) i) < 0) {
+ perror("***** ERROR: Create RX thread");
+ }
+ }
+ if (pthread_create(&tx_thread_id, NULL, tx_thread, NULL) < 0) {
+ perror("***** ERROR: Create TX thread");
+ }
+ if (pthread_create(&send_highprio_thread_id, NULL, send_highprio_thread, NULL) < 0) {
+ perror("***** ERROR: Create SendHighPrio thread");
+ }
+ if (pthread_create(&mic_thread_id, NULL, mic_thread, NULL) < 0) {
+ perror("***** ERROR: Create Mic thread");
+ }
+ if (pthread_create(&audio_thread_id, NULL, audio_thread, NULL) < 0) {
+ perror("***** ERROR: Create Audio thread");
+ }
+ } else {
+ pthread_join(ddc_specific_thread_id, NULL);
+ pthread_join(duc_specific_thread_id, NULL);
+ for (i=0; i<NUMRECEIVERS; i++) {
+ pthread_join(rx_thread_id[i], NULL);
+ }
+ pthread_join(send_highprio_thread_id, NULL);
+ pthread_join(tx_thread_id, NULL);
+ pthread_join(mic_thread_id, NULL);
+ pthread_join(audio_thread_id, NULL);
+ }
+ }
+ for (i=0; i<4; i++) {
+ rc=(buffer[4] >> (i+1)) & 0x01;
+ if (rc != ptt[i]) {
+ ptt[i]=rc;
+ fprintf(stderr,"HP: PTT%d=%d\n", i, rc);
+ }
+ }
+ rc=(buffer[5] >> 0) & 0x01;
+ if (rc != cwx) {
+ cwx=rc;
+ fprintf(stderr,"HP: CWX=%d\n", rc);
+ }
+ rc=(buffer[5] >> 1) & 0x01;
+ if (rc != dot) {
+ dot=rc;
+ fprintf(stderr,"HP: DOT=%d\n", rc);
+ }
+ rc=(buffer[5] >> 2) & 0x01;
+ if (rc != dash) {
+ dash=rc;
+ fprintf(stderr,"HP: DASH=%d\n", rc);
+ }
+ for (i=0; i<NUMRECEIVERS; i++) {
+ freq=(buffer[ 9+4*i] << 24) + (buffer[10+4*i] << 16) + (buffer[11+4*i] << 8) + buffer[12+4*i];
+ if (bits & 0x08) {
+ freq=round(122880000.0*(double) freq / 4294967296.0);
+ }
+ if (freq != rxfreq[i]) {
+ rxfreq[i]=freq;
+ fprintf(stderr,"HP: DDC%d freq: %lu\n", i, freq);
+ }
+ }
+ freq=(buffer[329] << 24) + (buffer[330] << 16) + (buffer[331] << 8) + buffer[332];
+ if (bits & 0x08) {
+ freq=round(122880000.0*(double) freq / 4294967296.0);
+ }
+ if (freq != txfreq) {
+ txfreq=freq;
+ fprintf(stderr,"HP: DUC freq: %lu\n", freq);
+ }
+ rc=buffer[345];
+ if (rc != txdrive) {
+ txdrive=rc;
+ txdrv_dbl=(double) txdrive * 0.003921568627;
+ fprintf(stderr,"HP: TX drive= %d (%f)\n", txdrive,txdrv_dbl);
+ }
+ rc=buffer[1400];
+ if (rc != w1400) {
+ w1400=rc;
+ fprintf(stderr,"HP: Xvtr/Audio enable=%x\n", rc);
+ }
+ rc=buffer[1401];
+ if (rc != ocout) {
+ ocout=rc;
+ fprintf(stderr,"HP: OC outputs=%x\n", rc);
+ }
+ rc=buffer[1402];
+ if (rc != db9) {
+ db9=rc;
+ fprintf(stderr,"HP: Outputs DB9=%x\n", rc);
+ }
+ rc=buffer[1403];
+ if (rc != mercury_atts) {
+ mercury_atts=rc;
+ fprintf(stderr,"HP: MercuryAtts=%x\n", rc);
+ }
+ // Store Alex0 and Alex1 bits in separate ints
+ freq=(buffer[1428] << 24) + (buffer[1429] << 16) + (buffer[1430] << 8) + buffer[1431];
+ for (i=0; i<32; i++) {
+ rc=(freq >> i) & 0x01;
+ if (rc != alex1[i]) {
+ alex1[i]=rc;
+ fprintf(stderr,"HP: ALEX1 bit%d set to %d\n", i, rc);
+ }
+ }
+ freq=(buffer[1432] << 24) + (buffer[1433] << 16) + (buffer[1434] << 8) + buffer[1435];
+ for (i=0; i<32; i++) {
+ rc=(freq >> i) & 0x01;
+ if (rc != alex0[i]) {
+ alex0[i]=rc;
+ fprintf(stderr,"HP: ALEX0 bit%d set to %d\n", i, rc);
+ }
+ }
+ rc=buffer[1442];
+ if (rc != stepatt1) {
+ stepatt1=rc;
+ rxatt1_dbl=pow(10.0, -0.05*stepatt1);
+ fprintf(stderr,"HP: StepAtt1 = %d\n", rc);
+ }
+ rc=buffer[1443];
+ if (rc != stepatt0) {
+ stepatt0=rc;
+ rxatt0_dbl=pow(10.0, -0.05*stepatt0);
+ fprintf(stderr,"HP: StepAtt0 = %d\n", stepatt0);
+ }
+ }
+ return NULL;
+}
+
+void *rx_thread(void *data) {
+ int sock;
+ struct sockaddr_in addr;
+ // One instance of this thread is started for each DDC
+ unsigned long seqnum;
+ unsigned char buffer[1444];
+ int yes = 1;
+ int rc;
+ int ddc;
+ int i;
+ unsigned long time;
+ long wait;
+ double i0sample,q0sample;
+ double i1sample,q1sample;
+ double irsample,qrsample;
+ double fac;
+ int sample;
+ unsigned char *p;
+ int noisept;
+ int myddc;
+ long myrate;
+ int sync,size;
+ int myadc, syncadc;
+ int ps=0;
+ int rxptr;
+
+ struct timespec delay;
+#ifdef __APPLE__
+ struct timespec now;
+#endif
+
+ myddc=(int) (uintptr_t) data;
+ if (myddc < 0 || myddc >= NUMRECEIVERS) return NULL;
+ seqnum=0;
+
+ sock=socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("***** ERROR: RXthread: socket");
+ return NULL;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(ddc0_port+myddc);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("***** ERROR: RXthread: bind");
+ return NULL;
+ }
+
+ noisept=0;
+ clock_gettime(CLOCK_MONOTONIC, &delay);
+ fprintf(stderr,"RX thread %d, enabled=%d\n", myddc, ddcenable[myddc]);
+ rxptr=txptr-5000;
+ if (rxptr < 0) rxptr += NEWRTXLEN;
+ while (run) {
+ if (!ddcenable[myddc] || rxrate[myddc] == 0 || rxfreq[myddc] == 0) {
+ usleep(5000);
+ clock_gettime(CLOCK_MONOTONIC, &delay);
+ rxptr=txptr-5000;
+ if (rxptr < 0) rxptr += NEWRTXLEN;
+ continue;
+ }
+ myadc=adcmap[myddc];
+ // for simplicity, we only allow for a single "synchronized" DDC,
+ // this well covers the PURESIGNAL and DIVERSITY cases
+ sync=0;
+ i=syncddc[myddc];
+ while (i) {
+ sync++;
+ i = i >> 1;
+ }
+ // sync == 0 means no synchronizatsion
+ // sync == 1,2,3 means synchronization with DDC0,1,2
+ // Usually we send 238 samples per buffer, but with synchronization
+ // we send 119 sample *pairs*.
+ if (sync) {
+ size=119;
+ wait=119000000L/rxrate[myddc]; // time for these samples in nano-secs
+ syncadc=adcmap[sync-1];
+ } else {
+ size=238;
+ wait=238000000L/rxrate[myddc]; // time for these samples in nano-secs
+ }
+ //
+ // ADC0: noise (+ distorted TX signal upon TXing)
+ // ADC1: noise 20 dB stronger
+ // ADC2: original TX signal (ADC1 on HERMES)
+ //
+
+ ps=(sync && (rxrate[myadc]==192) && ptt[0] && (syncadc == adc));
+ p=buffer;
+ *p++ =(seqnum >> 24) & 0xFF;
+ *p++ =(seqnum >> 16) & 0xFF;
+ *p++ =(seqnum >> 8) & 0xFF;
+ *p++ =(seqnum >> 0) & 0xFF;
+ seqnum += 1;
+ // do not use time stamps
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ // 24 bits per sample *ALWAYS*
+ *p++ = 0;
+ *p++ = 24;
+ *p++ = 0;
+ *p++ = sync ? 2*size : size; // should be 238 in either case
+ for (i=0; i<size; i++) {
+ //
+ // produce noise depending on the ADC
+ //
+ i1sample=i0sample=noiseItab[noisept];
+ q1sample=q0sample=noiseItab[noisept++];
+ if (noisept == LENNOISE) noisept=rand() / NOISEDIV;
+ if (myadc == 1) {
+ i0sample=i0sample*10.0; // 20 dB more noise on ADC1
+ q0sample=q0sample*10.0;
+ }
+ //
+ // PS: produce sample PAIRS,
+ // a) distorted TX data (with Drive and Attenuation)
+ // b) original TX data (normalized)
+ //
+ if (ps) {
+ irsample = isample[rxptr];
+ qrsample = qsample[rxptr++];
+ if (rxptr >= NEWRTXLEN) rxptr=0;
+ fac=txatt_dbl*txdrv_dbl*(IM3a+IM3b*(irsample*irsample+qrsample*qrsample)*txdrv_dbl*txdrv_dbl);
+ if (myadc == 0) {
+ i0sample += irsample*fac;
+ q0sample += qrsample*fac;
+ }
+ }
+ if (sync) {
+ if (ps) {
+ // synchronized stream: undistorted TX signal with constant max. amplitude
+ i1sample = irsample * 0.329;
+ q1sample = qrsample * 0.329;
+ }
+ sample=i0sample * 8388607.0;
+ *p++=(sample >> 16) & 0xFF;
+ *p++=(sample >> 8) & 0xFF;
+ *p++=(sample >> 0) & 0xFF;
+ sample=q0sample * 8388607.0;
+ *p++=(sample >> 16) & 0xFF;
+ *p++=(sample >> 8) & 0xFF;
+ *p++=(sample >> 0) & 0xFF;
+ sample=i1sample * 8388607.0;
+ *p++=(sample >> 16) & 0xFF;
+ *p++=(sample >> 8) & 0xFF;
+ *p++=(sample >> 0) & 0xFF;
+ sample=q1sample * 8388607.0;
+ *p++=(sample >> 16) & 0xFF;
+ *p++=(sample >> 8) & 0xFF;
+ *p++=(sample >> 0) & 0xFF;
+ } else {
+ sample=i0sample * 8388607.0;
+ *p++=(sample >> 16) & 0xFF;
+ *p++=(sample >> 8) & 0xFF;
+ *p++=(sample >> 0) & 0xFF;
+ sample=q0sample * 8388607.0;
+ *p++=(sample >> 16) & 0xFF;
+ *p++=(sample >> 8) & 0xFF;
+ *p++=(sample >> 0) & 0xFF;
+ }
+ }
+ delay.tv_nsec += wait;
+ while (delay.tv_nsec >= 1000000000) {
+ delay.tv_nsec -= 1000000000;
+ delay.tv_sec++;
+ }
+#ifdef __APPLE__
+ //
+ // The (so-called) operating system for Mac does not have clock_nanosleep(),
+ // but is has clock_gettime as well as nanosleep.
+ // So, to circumvent this problem, we look at the watch and determine
+ // how long we should sleep now.
+ //
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ now.tv_sec =delay.tv_sec - now.tv_sec;
+ now.tv_nsec=delay.tv_nsec - now.tv_nsec;
+ while (now.tv_nsec < 0) {
+ now.tv_nsec += 1000000000;
+ now.tv_sec--;
+ }
+ nanosleep(&now, NULL);
+#else
+ clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &delay, NULL);
+#endif
+ if (sendto(sock, buffer, 1444, 0, (struct sockaddr*)&addr_new, sizeof(addr_new)) < 0) {
+ perror("***** ERROR: RX thread sendto");
+ break;
+ }
+ }
+ close(sock);
+ return NULL;
+}
+
+//
+// This thread receives data (TX samples) from the PC
+//
+void *tx_thread(void * data) {
+ int sock;
+ struct sockaddr_in addr;
+ socklen_t lenaddr=sizeof(addr);
+ unsigned long seqnum, seqold;
+ unsigned char buffer[1444];
+ int yes = 1;
+ int rc;
+ int i;
+ unsigned char *p;
+ int noisept;
+ int sample;
+ double di,dq;
+ double sum;
+ struct timeval tv;
+
+ sock=socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("***** ERROR: TX: socket");
+ return NULL;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(duc0_port);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("***** ERROR: TX: bind");
+ return NULL;
+ }
+
+ seqnum=0;
+ while(run) {
+ rc = recvfrom(sock, buffer, 1444, 0,(struct sockaddr *)&addr, &lenaddr);
+ if (rc < 0 && errno != EAGAIN) {
+ perror("***** ERROR: TX thread: recvmsg");
+ break;
+ }
+ if (rc < 0) continue;
+ if (rc != 1444) {
+ fprintf(stderr,"Received TX packet with incorrect length");
+ break;
+ }
+ seqold = seqnum;
+ seqnum = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+ if (seqnum != 0 &&seqnum != seqold+1 ) {
+ fprintf(stderr,"TXthread: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+ }
+ p=buffer+4;
+ sum=0.0;
+ for (i=0; i<240; i++) {
+ // process 240 TX iq samples
+ sample = (int)((signed char) (*p++))<<16;
+ sample |= (int)((((unsigned char)(*p++))<<8)&0xFF00);
+ sample |= (int)((unsigned char)(*p++)&0xFF);
+ di = (double) sample / 8388608.0;
+ sample = (int)((signed char) (*p++))<<16;
+ sample |= (int)((((unsigned char)(*p++))<<8)&0xFF00);
+ sample |= (int)((unsigned char)(*p++)&0xFF);
+ dq = (double) sample / 8388608.0;
+//
+// put TX samples into ring buffer
+//
+ isample[txptr]=di;
+ qsample[txptr++]=dq;
+ if (txptr >= NEWRTXLEN) txptr=0;
+//
+// accumulate TX power
+//
+ sum += (di*di+dq*dq);
+ }
+ txlevel=sum * txdrv_dbl * txdrv_dbl * 0.0041667;
+ fprintf(stderr,"LEV1=%f LEV2=%f\n", sum*0.0041667, txlevel);
+ }
+ return NULL;
+}
+
+void *send_highprio_thread(void *data) {
+ int sock;
+ struct sockaddr_in addr;
+ unsigned long seqnum;
+ unsigned char buffer[60];
+ int yes = 1;
+ int rc;
+ int i;
+ unsigned char *p;
+
+
+ seqnum=0;
+
+ sock=socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("***** ERROR: SendHighPrio thread: socket");
+ return NULL;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(shp_port);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("***** ERROR: SendHighPrio thread: bind");
+ return NULL;
+ }
+
+ seqnum=0;
+ while (1) {
+ if (!run) {
+ close(sock);
+ break;
+ }
+ // prepare buffer
+ memset(buffer, 0, 60);
+ p=buffer;
+ *p++ = (seqnum >> 24) & 0xFF;
+ *p++ = (seqnum >> 16) & 0xFF;
+ *p++ = (seqnum >> 8) & 0xFF;
+ *p++ = (seqnum >> 0) & 0xFF;
+ *p++ = 0; // no PTT and CW attached
+ *p++ = 0; // no ADC overload
+ *p++ = 1;
+ *p++ = 126; // 1 W exciter power
+
+ p +=6;
+
+ rc=(int) (800.0*sqrt(10*txlevel));
+ *p++ = (rc >> 8) & 0xFF;
+ *p++ = (rc ) & 0xFF;
+
+ buffer[49]=63; // about 13 volts supply
+
+ if (sendto(sock, buffer, 60, 0, (struct sockaddr*)&addr_new, sizeof(addr_new)) < 0) {
+ perror("***** ERROR: HP send thread sendto");
+ break;
+ }
+ seqnum++;
+ usleep(50000); // wait 50 msec
+ }
+ close(sock);
+ return NULL;
+}
+
+//
+// This thread receives the audio samples and plays them
+//
+void *audio_thread(void *data) {
+ int sock;
+ struct sockaddr_in addr;
+ socklen_t lenaddr=sizeof(addr);
+ unsigned long seqnum, seqold;
+ unsigned char buffer[260];
+ int yes = 1;
+ int rc;
+ int i;
+ unsigned char *p;
+ int16_t lsample,rsample;
+ struct timeval tv;
+
+ sock=socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("***** ERROR: Audio: socket");
+ return NULL;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(audio_port);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("***** ERROR: Audio: bind");
+ return NULL;
+ }
+
+ seqnum=0;
+ while(run) {
+ rc = recvfrom(sock, buffer, 260, 0,(struct sockaddr *)&addr, &lenaddr);
+ if (rc < 0 && errno != EAGAIN) {
+ perror("***** ERROR: Audio thread: recvmsg");
+ break;
+ }
+ if (rc < 0) continue;
+ if (rc != 260) {
+ fprintf(stderr,"Received Audio packet with incorrect length");
+ break;
+ }
+ seqold = seqnum;
+ seqnum = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
+ if (seqnum != 0 &&seqnum != seqold+1 ) {
+ fprintf(stderr,"Audio thread: SEQ ERROR, old=%lu new=%lu\n", seqold, seqnum);
+ }
+ p=buffer+4;
+ for (i=0; i<64; i++) {
+ lsample = ((signed char) *p++) << 8;
+ lsample |= (*p++ & 0xff);
+ rsample = ((signed char) *p++) << 8;
+ rsample |= (*p++ & 0xff);
+ audio_write(lsample,rsample);
+ }
+ }
+ close (sock);
+ return NULL;
+}
+
+//
+// The microphone thread generates
+// a two-tone signal
+//
+void *mic_thread(void *data) {
+ int sock;
+ struct sockaddr_in addr;
+ unsigned long seqnum;
+ unsigned char buffer[132];
+ unsigned char *p;
+ int yes = 1;
+ int rc;
+ int i;
+ double arg1,arg2;
+ int sintab[480]; // microphone data
+ int sinptr = 0;
+ struct timespec delay;
+#ifdef __APPLE__
+ struct timespec now;
+#endif
+
+#define FREQ900 0.11780972450961724644234912687298
+#define FREQ1700 0.22252947962927702105777057298230
+
+ seqnum=0;
+ arg1=0.0;
+ arg2=0.0;
+ for (i=0; i<480; i++) {
+ sintab[i]=(int) round((sin(arg1)+sin(arg2)) * 5000.0);
+ arg1 += FREQ900;
+ arg2 += FREQ1700;
+ }
+
+ sock=socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ perror("***** ERROR: Mic thread: socket");
+ return NULL;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&yes, sizeof(yes));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ addr.sin_port = htons(mic_port);
+
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("***** ERROR: Mic thread: bind");
+ return NULL;
+ }
+
+ seqnum=0;
+ memset(buffer, 0, 132);
+ clock_gettime(CLOCK_MONOTONIC, &delay);
+ while (run) {
+ // update seq number
+ p=buffer;
+ *p++ = (seqnum >> 24) & 0xFF;
+ *p++ = (seqnum >> 16) & 0xFF;
+ *p++ = (seqnum >> 8) & 0xFF;
+ *p++ = (seqnum >> 0) & 0xFF;
+ seqnum++;
+ // take periodic data from sintab as "microphone samples"
+ for (i=0; i< 64; i++) {
+ rc=sintab[sinptr++];
+ if (sinptr == 480) sinptr=0;
+ *p++ = (rc >> 8) & 0xff;
+ *p++ = (rc & 0xff);
+ }
+ // 64 samples with 48000 kHz, makes 1333333 nsec
+ delay.tv_nsec += 1333333;
+ while (delay.tv_nsec >= 1000000000) {
+ delay.tv_nsec -= 1000000000;
+ delay.tv_sec++;
+ }
+#ifdef __APPLE__
+ //
+ // The (so-called) operating system for Mac does not have clock_nanosleep(),
+ // but is has clock_gettime as well as nanosleep.
+ // So, to circumvent this problem, we look at the watch and determine
+ // how long we should sleep now.
+ //
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ now.tv_sec =delay.tv_sec - now.tv_sec;
+ now.tv_nsec=delay.tv_nsec - now.tv_nsec;
+ while (now.tv_nsec < 0) {
+ now.tv_nsec += 1000000000;
+ now.tv_sec--;
+ }
+ nanosleep(&now, NULL);
+#else
+ clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &delay, NULL);
+#endif
+ if (sendto(sock, buffer, 132, 0, (struct sockaddr*)&addr_new, sizeof(addr_new)) < 0) {
+ perror("***** ERROR: Mic thread sendto");
+ break;
+ }
+ }
+ close(sock);
+ return NULL;
+}
+
+