From: c vw Date: Wed, 17 Jul 2019 14:29:48 +0000 (+0200) Subject: HPSDR simulator extended to "New Protocol" X-Git-Url: https://git.rkrishnan.org/%5B/listings/flags/status?a=commitdiff_plain;h=934cfb2144844357f1fda2997f1757c3e64381f6;p=pihpsdr.git HPSDR simulator extended to "New Protocol" --- diff --git a/Makefile b/Makefile index ad0dbd8..652f9bb 100644 --- a/Makefile +++ b/Makefile @@ -475,7 +475,10 @@ release: $(PROGRAM) ############################################################################# hpsdrsim.o: hpsdrsim.c - $(CC) -c -O hpsdrsim.c + $(CC) -c -O -DALSASOUND hpsdrsim.c -hpsdrsim: hpsdrsim.o - $(LINK) -o hpsdrsim hpsdrsim.o $(AUDIO_LIBS) -lm -lpthread +newhpsdrsim.o: newhpsdrsim.c + $(CC) -c -O newhpsdrsim.c + +hpsdrsim: hpsdrsim.o newhpsdrsim.o + $(LINK) -o hpsdrsim hpsdrsim.o newhpsdrsim.o -lasound -lm -lpthread diff --git a/hpsdrsim.c b/hpsdrsim.c index 0b73aef..358c74a 100644 --- a/hpsdrsim.c +++ b/hpsdrsim.c @@ -1,5 +1,3 @@ -//#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 * @@ -34,7 +32,7 @@ * 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" @@ -59,10 +57,15 @@ #include #include +#define NEED_DUMMY_AUDIO 1 + #ifdef PORTAUDIO #include "portaudio.h" -#else +#undef NEED_DUMMY_AUDIO +#endif +#ifdef ALSASOUND #include +#undef NEED_DUMMY_AUDIO #endif // Forward declarations for the audio functions @@ -82,6 +85,18 @@ extern int clock_nanosleep(clockid_t __clock_id, int __flags, 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. @@ -150,33 +165,12 @@ static double txdrv_dbl = 0.99; 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 @@ -197,23 +191,37 @@ void *handler_ep6(void *arg); // 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 }; @@ -221,14 +229,15 @@ int main(int argc, char *argv[]) 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 @@ -236,17 +245,18 @@ int main(int argc, char *argv[]) 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= 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(); /* @@ -257,25 +267,28 @@ int main(int argc, char *argv[]) * 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; @@ -289,18 +302,18 @@ int main(int argc, char *argv[]) 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; @@ -325,7 +338,7 @@ int main(int argc, char *argv[]) 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; @@ -339,16 +352,7 @@ int main(int argc, char *argv[]) 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; @@ -405,7 +409,8 @@ int main(int argc, char *argv[]) } 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; @@ -439,8 +444,8 @@ int main(int argc, char *argv[]) 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) @@ -472,7 +477,9 @@ int main(int argc, char *argv[]) // 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; @@ -482,25 +489,12 @@ int main(int argc, char *argv[]) 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; @@ -551,13 +545,14 @@ int main(int argc, char *argv[]) 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); @@ -589,13 +584,13 @@ int main(int argc, char *argv[]) } 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); @@ -616,18 +611,16 @@ int main(int argc, char *argv[]) } 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); @@ -640,10 +633,10 @@ int main(int argc, char *argv[]) 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 @@ -659,27 +652,175 @@ int main(int argc, char *argv[]) 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) { @@ -728,7 +869,7 @@ void process_ep2(uint8_t *frame) 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! @@ -793,7 +934,7 @@ void process_ep2(uint8_t *frame) 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: @@ -817,11 +958,7 @@ void process_ep2(uint8_t *frame) 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])); @@ -861,7 +998,7 @@ void process_ep2(uint8_t *frame) 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; @@ -885,15 +1022,6 @@ void process_ep2(uint8_t *frame) } } -// -// 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]; @@ -904,11 +1032,8 @@ void *handler_ep6(void *arg) 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] = { @@ -936,17 +1061,7 @@ void *handler_ep6(void *arg) 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; @@ -954,13 +1069,6 @@ void *handler_ep6(void *arg) // // Produce RX data // - // a) noise from a 16-bit ADC - // - k=RAND_MAX / 2; - for (i=0; i> 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; @@ -1144,14 +1236,14 @@ void *handler_ep6(void *arg) 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)); } } @@ -1251,8 +1343,8 @@ void audio_write (int16_t l, int16_t r) } } } - -#else +#endif +#ifdef ALSASOUND // // Audio functions based on LINUX ALSA // @@ -1460,3 +1552,18 @@ void audio_write(int16_t left_sample,int16_t right_sample) { } } #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 diff --git a/newhpsdrsim.c b/newhpsdrsim.c new file mode 100644 index 0000000..cf96e73 --- /dev/null +++ b/newhpsdrsim.c @@ -0,0 +1,1247 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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> (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> (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> 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= 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; +} + +