#include #include #include #include #include #include #include "quisk.h" #include #include "microphone.h" #include "filter.h" #include "freedv.h" #ifdef MS_WINDOWS #include static int mic_cleanup = 0; // must clean up winsock #else #include #include #include #endif #define DEBUG 0 #define DEBUG_LEVEL 0 #if DEBUG_LEVEL || DEBUG_IO || DEBUG static int debug_timer = 1; // count up number of samples #endif // The microphone samples must be 48000 sps or 8000 sps. The output sample // rate is always MIC_OUT_RATE samples per second // FM needs pre-emphasis and de-emphasis. See vk1od.net/FM/FM.htm for details. // For IIR design, see http://www.abvolt.com/research/publications2.htm. // Microhone preemphasis: boost high frequencies 0.00 to 1.00 double quisk_mic_preemphasis; // Microphone clipping; try 3.0 or 4.0 double quisk_mic_clip; // If true, decimate 48000 sps mic samples to 8000 sps for processing #define DECIM_8000 1 struct alc { complex double * buffer; int buf_size; int index; int block_index; int counter; int fault; double max_magn; double gain_now[20]; double gain_max; double gain_min; double gain_change; double next_change; double final_gain; } ; // These are external: int mic_max_display; // display value of maximum microphone signal level 0 to 2**15 - 1 int quiskSpotLevel = -1; // level is -1 for Spot button Off; else the Spot level 0 to 1000. int quiskImdLevel = 500; // level for rxMode IMD, 0 to 1000 static SOCKET mic_socket = INVALID_SOCKET; // send microphone samples to a socket static double mic_agc_level = 0.10; // Mic levels below this are noise and are ignored static int mic_level; // maximum microphone signal level for display static int mic_timer; // time to display maximum mic level static int align4; // add two bytes to start of audio samples to align to 4 bytes static double modulation_index = 1.6; // For FM transmit, the modulation index static int is_vox = 0; // Is the VOX level exceeded? static int vox_level = CLIP16; // VOX trigger level as a number 0 to CLIP16 static int timeVOX = 2000; // VOX hang time in milliseconds static int tx_sample_rate = 48000; // Used for SoapySDR static int reverse_tx_sideband; static int doTxCorrect = 0; // Corrections for UDP sample transmit static double TxCorrectLevel; static complex TxCorrectDc; // Used for the Hermes protocol #define HERMES_TX_BUF_SAMPLES 4800 // buffer size in I/Q samples (two shorts) #define HERMES_TX_BUF_SHORTS (HERMES_TX_BUF_SAMPLES * 2) static int hermes_read_index; // index to read from buffer static int hermes_write_index; // index to write to buffer static int hermes_num_samples; // number of samples in the buffer static short hermes_buf[HERMES_TX_BUF_SHORTS]; // buffer to store Tx I/Q samples waiting to be sent at 48 ksps static int hermes_filter_rx; // hermes filter to use for Rx static int hermes_filter_tx; // hermes filter to use for Tx static int alex_hpf_rx; // Alex HPF to use for Rx static int alex_hpf_tx; // Alex HPF to use for Tx static int alex_lpf_rx; // Alex LPF to use for Rx static int alex_lpf_tx; // Alex LPF to use for Tx #define TX_BLOCK_SHORTS 600 // transmit UDP packet with this many shorts (two bytes) (perhaps + 1) #define MIC_MAX_HOLD_TIME 400 // Time to hold the maximum mic level on the Status screen in milliseconds // If USE_GET_SIN is not zero, replace mic samples with a sin wave at a // frequency determined by the sidetone slider and an amplitude determined // by the Spot button level. LEVEL FAILS // If USE_GET_SIN is 1, pass these samples through the transmit filters. // If USE_GET_SIN is 2, transmit these samples directly. #define USE_GET_SIN 0 // If USE_2TONE is not zero, replace samples with a 2-tone test signal. #define USE_2TONE 0 #if USE_GET_SIN static void get_sin(complex double * cSamples, int count) { // replace mic samples with a sin wave int i; double freq; complex double phase1; // Phase increment static complex double vector1 = CLIP32 / 2; // Use the sidetone slider 0 to 1000 to set frequency //freq = (quisk_sidetoneCtrl - 500) / 1000.0 * MIC_OUT_RATE; freq = quisk_sidetoneCtrl * 5; freq = ((int)freq / 50) * 50; #if USE_GET_SIN == 2 phase1 = cexp(I * 2.0 * M_PI * freq / MIC_OUT_RATE); count *= MIC_OUT_RATE / quisk_sound_state.mic_sample_rate; #else phase1 = cexp(I * 2.0 * M_PI * freq / quisk_sound_state.mic_sample_rate); #endif for (i = 0; i < count; i++) { vector1 *= phase1; cSamples[i] = vector1; } #if DEBUG_IO || DEBUG if (debug_timer == 0) printf ("get_sin freq %.0lf\n", freq); #endif } #endif #if USE_2TONE static void get_2tone(complex double * cSamples, int count) { // replace mic samples int i; static complex double phase1=0, phase2; // Phase increment static complex double vector1; static complex double vector2; if (phase1 == 0) { // initialize phase1 = cexp((I * 2.0 * M_PI * IMD_TONE_1) / quisk_sound_state.mic_sample_rate); phase2 = cexp((I * 2.0 * M_PI * IMD_TONE_2) / quisk_sound_state.mic_sample_rate); vector1 = CLIP32 / 2.0; vector2 = CLIP32 / 2.0; } for (i = 0; i < count; i++) { vector1 *= phase1; vector2 *= phase2; cSamples[i] = (vector1 + vector2); } } #endif static double CcmPeak(double * dsamples, complex double * csamples, int count) { int i, j; complex double csample; double dtmp, dsample, newlevel, oldlevel; static double out_short, out_long; static struct Ccmpr { int buf_size; int index_read; double themax; double level; double * d_samp; complex double * c_samp; double * levl; } dat = {0}; if ( ! dat.buf_size) { // initialize; the sample rate is 8000 dat.buf_size = 8000 * 30 / 1000; // total delay in samples dat.index_read = 0; // index to output; and then write a new sample here dat.themax = 1.0; // maximum level in the buffer dat.level = 1.0; // current output level dat.d_samp = (double *) malloc(dat.buf_size * sizeof(double)); // buffer for double samples dat.c_samp = (complex double *) malloc(dat.buf_size * sizeof(complex double)); // buffer for complex samples dat.levl = (double *) malloc(dat.buf_size * sizeof(double)); // magnitude of the samples for (i = 0; i < dat.buf_size; i++) { dat.d_samp[i] = 0; dat.c_samp[i] = 0; dat.levl[i] = 1.0; } dtmp = 1.0 / 8000; // sample time out_short = 1.0 - exp(- dtmp / 0.010); // short time constant out_long = 1.0 - exp(- dtmp / 3.000); // long time constant return 1.0; } for (i = 0; i < count; i++) { if (dsamples) { dsample = dsamples[i]; dsamples[i] = dat.d_samp[dat.index_read] / dat.level; // FIFO output dat.d_samp[dat.index_read] = dsample; // write new sample at read index newlevel = fabs(dsample); } else { csample = csamples[i]; csamples[i] = dat.c_samp[dat.index_read] / dat.level; // FIFO output dat.c_samp[dat.index_read] = csample; // write new sample at read index newlevel = cabs(csample); } oldlevel = dat.levl[dat.index_read]; dat.levl[dat.index_read] = newlevel; if (newlevel < dat.themax && oldlevel < dat.themax) { // some other sample is the maximum // no change to dat.themax } else if (newlevel > dat.themax && newlevel > oldlevel) { // newlevel is the maximum dat.themax = newlevel; } else { // search for the maximum level dat.themax = 0; // Find the maximim level in the buffer for (j = 0; j < dat.buf_size; j++) { if (dat.levl[j] > dat.themax) dat.themax = dat.levl[j]; } } // Increase dat.level if the maximum level is greater than 1.0; // decrease it slowly back to 1.0 if it is lower. Output is modulated by dat.level. if (dat.themax > 1.0) // increase rapidly to the peak level dat.level = dat.level * (1.0 - out_short) + dat.themax * out_short; else // decrease slowly back to 1.0 dat.level = dat.level * (1.0 - out_long) + 1.0 * out_long; if (++dat.index_read >= dat.buf_size) dat.index_read = 0; } return dat.level; } static void init_alc(struct alc * pt, int size) { // Call first to set the buffer size. Then call to initialize the structure on each key down. int i; if (pt->buffer == NULL) { pt->buf_size = size; // do not change the size pt->buffer = (complex double *)malloc(size * sizeof(complex double)); for (i = 0; i < 20; i++) // initial gain by rx_mode switch(i) { case DGT_U: case DGT_L: case DGT_IQ: pt->gain_now[i] = 1.4; break; case FDV_U: case FDV_L: pt->gain_now[i] = 2.0; break; default: pt->gain_now[i] = 1.0; break; } } pt->index = 0; pt->block_index = 0; pt->counter = 0; pt->fault = 0; pt->max_magn = 0; pt->gain_max = 3.0; pt->gain_min = 0.1; pt->gain_change = 0; pt->next_change = 0; pt->final_gain = 0; for (i = 0; i < pt->buf_size; i++) pt->buffer[i] = 0; } static void process_alc(complex double * cSamples, int count, struct alc * pt, rx_mode_type rx_mode) { // Automatic Level Control (ALC) int i; double d, magn; complex double csamp; for (i = 0; i < count; i++) { csamp = cSamples[i]; // new sample to add to buffer cSamples[i] = pt->buffer[pt->index] * pt->gain_now[rx_mode]; // remove sample from buffer and apply gain #if DEBUG_LEVEL || DEBUG_IO || DEBUG magn = cabs(cSamples[i]); if (magn >= CLIP16) printf("ALC clip gain %9.6f level %9.6f\n", pt->gain_now[rx_mode], magn / CLIP16); #endif pt->buffer[pt->index] = csamp; // add new sample to buffer magn = cabs(csamp); // measure new sample if (magn * (pt->gain_now[rx_mode] + pt->gain_change * pt->buf_size) > (CLIP16 - 10)) { pt->gain_change = ((CLIP16 - 10) / magn - pt->gain_now[rx_mode]) / pt->buf_size; pt->final_gain = pt->gain_now[rx_mode] + pt->gain_change * pt->buf_size; if (pt->final_gain > pt->gain_max) { pt->final_gain = pt->gain_max; pt->gain_change = (pt->final_gain - pt->gain_now[rx_mode]) / pt->buf_size; } else if (pt->final_gain < pt->gain_min) { pt->final_gain = pt->gain_min; pt->gain_change = (pt->final_gain - pt->gain_now[rx_mode]) / pt->buf_size; } pt->block_index = pt->index; pt->counter = 0; pt->fault = 0; pt->next_change = 1E10; //printf("ALC DEC: gain %9.6f change %9.6f final gain %9.6f\n", pt->gain_now[rx_mode], pt->gain_change, pt->final_gain); } else if (pt->index == pt->block_index) { //d = cabs(cSamples[i]) / (CLIP16 - 10); //printf("ALC Fin: gain %9.6f out level %9.6f\n", pt->gain_now[rx_mode], d); #if 0 alc_start_gain = alc_gain; k = alc_block_index; for (j = 1; j < pt->buf_size; j++) { if (++k >= pt->buf_size) k = 0; magn = cabs(alc_buffer[k]); if (magn < 100) { alc_fault++; continue; } else { d = ((CLIP16 - 10) / magn - alc_start_gain) / j; if ( alc_next_change > d) alc_next_change = d; } } #endif d = 5.0; // number of seconds to double gain d = 1.0 / (48000.0 * d); if (pt->next_change > d) pt->next_change = d; if (pt->next_change != 1E10 && pt->fault < pt->buf_size - 10) { pt->gain_change = pt->next_change; } pt->final_gain = pt->gain_now[rx_mode] + pt->gain_change * pt->buf_size; if (pt->final_gain > pt->gain_max) { pt->final_gain = pt->gain_max; pt->gain_change = (pt->final_gain - pt->gain_now[rx_mode]) / pt->buf_size; } else if (pt->final_gain < pt->gain_min) { pt->final_gain = pt->gain_min; pt->gain_change = (pt->final_gain - pt->gain_now[rx_mode]) / pt->buf_size; } //printf("ALC New: gain %9.6f change %9.6f final gain %9.6f\n", pt->gain_now[rx_mode], pt->gain_change, pt->final_gain); pt->fault = 0; pt->counter = 0; pt->next_change = 1E10; } else { if (magn < 100) { pt->fault++; } else { d = ((CLIP16 - 10) / magn - pt->final_gain) / ++pt->counter; if ( pt->next_change > d) pt->next_change = d; } } pt->gain_now[rx_mode] += pt->gain_change; if (++pt->index >= pt->buf_size) pt->index = 0; } #if DEBUG_LEVEL || DEBUG_IO || DEBUG for (i = 0; i < count; i++) { magn = cabs(cSamples[i]); if (pt->max_magn < magn) pt->max_magn = magn; } if (debug_timer == 0) { printf("ALC Out: gain %9.6f max lvl%9.6f final gain %9.6f\n", pt->gain_now[rx_mode], pt->max_magn / CLIP16, pt->final_gain); pt->max_magn = 0; } #endif } static int tx_filter(complex double * filtered, int count) { // Input samples are creal(filtered), output is filtered. The input rate must be 8000 or 48000 sps. int i, is_ssb; int sample_rate = 8000; double dsample, dtmp, magn; complex double csample; static double inMax=0.3; static double x_1=0; static double aaa, bbb, ccc, Xmin, Xmax, Ymax; static int samples_size = 0; static double * dsamples = NULL; static complex double * csamples = NULL; static double time_long, time_short; static struct quisk_dFilter filtDecim, dfiltInterp; static struct quisk_dFilter filtAudio1, filtAudio2, dfiltAudio3; static struct quisk_cFilter cfiltAudio3, cfiltInterp; static struct quisk_dFilter filter1={NULL}, filter2; #if DEBUG_IO || DEBUG char * clip; static double dbOut = 0, Level0 = 0, Level1 = 0, Level2 = 0, Level3 = 0, Level4 = 0; #endif is_ssb = (rxMode == LSB || rxMode == USB); if (!filtered) { // initialization if (! filter1.dCoefs) { quisk_filt_dInit(&filter1, quiskMicFilt8Coefs, sizeof(quiskMicFilt8Coefs)/sizeof(double)); quisk_filt_dInit(&filter2, quiskMicFilt8Coefs, sizeof(quiskMicFilt8Coefs)/sizeof(double)); quisk_filt_dInit(&filtDecim, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double)); quisk_filt_dInit(&dfiltInterp, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double)); quisk_filt_cInit(&cfiltInterp, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double)); quisk_filt_dInit(&filtAudio1, quiskFiltTx8kAudioB, sizeof(quiskFiltTx8kAudioB)/sizeof(double)); quisk_filt_dInit(&filtAudio2, quiskFiltTx8kAudioB, sizeof(quiskFiltTx8kAudioB)/sizeof(double)); quisk_filt_dInit(&dfiltAudio3, quiskFiltTx8kAudioB, sizeof(quiskFiltTx8kAudioB)/sizeof(double)); quisk_filt_cInit(&cfiltAudio3, quiskFiltTx8kAudioB, sizeof(quiskFiltTx8kAudioB)/sizeof(double)); dtmp = 1.0 / sample_rate; // sample time time_long = 1.0 - exp(- dtmp / 3.000); time_short = 1.0 - exp(- dtmp / 0.005); Ymax = pow(10.0, - 1 / 20.0); // maximum y Xmax = pow(10.0, 3 / 20.0); // x where slope is zero; for x > Xmax, y == Ymax Xmin = Ymax - fabs(Ymax - Xmax); // x where slope is 1 and y = x; start of compression aaa = 1.0 / (2.0 * (Xmin - Xmax)); // quadratic bbb = -2.0 * aaa * Xmax; ccc = Ymax - aaa * Xmax * Xmax - bbb * Xmax; #if DEBUG_IO || DEBUG printf("Compress to %.2lf dB from %.2lf to %.2lf dB\n", 20 * log10(Ymax), 20 * log10(Xmin), 20 * log10(Xmax)); #endif } if (is_ssb) { quisk_filt_tune(&filter1, 1650.0 / sample_rate, rxMode != LSB); quisk_filt_tune(&filter2, 1650.0 / sample_rate, rxMode != LSB); } return 0; } // check size of dsamples[] and csamples[] buffer if (count > samples_size) { samples_size = count * 2; if (dsamples) free(dsamples); if (csamples) free(csamples); dsamples = (double *)malloc(samples_size * sizeof(double)); csamples = (complex double *)malloc(samples_size * sizeof(complex double)); } // copy to dsamples[], normalize to +/- 1.0 for (i = 0; i < count; i++) dsamples[i] = creal(filtered[i]) / CLIP16; // Decimate to 8000 Hz if (quisk_sound_state.mic_sample_rate != sample_rate) count = quisk_dDecimate(dsamples, count, &filtDecim, quisk_sound_state.mic_sample_rate / sample_rate); // restrict bandwidth 300 to 2700 Hz count = quisk_dFilter(dsamples, count, &filtAudio1); #if DEBUG_IO || DEBUG // Measure peak input audio level for (i = 0; i < count; i++) { magn = fabs(dsamples[i]); if (magn > Level0) Level0 = magn; } #endif // high pass filter for preemphasis: See Radcom, January 2010, page 76. // quisk_mic_preemphasis == 1 was measured as 6 dB / octave. // gain at 800 Hz was measured as 0.104672. for (i = 0; i < count; i++) { dtmp = dsamples[i]; dsamples[i] = dtmp - quisk_mic_preemphasis * x_1; x_1 = dtmp; // delayed sample dsamples[i] *= 2; // compensate for loss #if DEBUG_IO || DEBUG magn = fabs(dsamples[i]); if (magn > Level1) Level1 = magn; #endif } if (is_ssb) { // SSB // FIR bandpass filter; separate into I and Q for (i = 0; i < count; i++) { csample = quisk_dC_out(dsamples[i], &filter1) * 2.0; // filter loss 0.5 // Measure average peak input audio level and normalize magn = cabs(csample); if (magn > inMax) inMax = inMax * (1 - time_short) + time_short * magn; else if(magn > mic_agc_level) inMax = inMax * (1 - time_long) + time_long * magn; else inMax = inMax * (1 - time_long) + time_long * mic_agc_level; csample /= inMax; magn /= inMax; #if DEBUG_IO || DEBUG if (magn > Level2) Level2 = magn; #endif // Audio compression. csample *= quisk_mic_clip; magn *= quisk_mic_clip; if (magn > 1.0) csample = csample / magn; dsamples[i] = creal(csample); } } else { // AM and FM // Measure average peak input audio level and normalize for (i = 0; i < count; i++) { dsample = dsamples[i]; magn = fabs(dsample); if (magn > inMax) inMax = inMax * (1 - time_short) + time_short * magn; else if(magn > mic_agc_level) inMax = inMax * (1 - time_long) + time_long * magn; else inMax = inMax * (1 - time_long) + time_long * mic_agc_level; dsample /= inMax; magn /= inMax; #if DEBUG_IO || DEBUG if (magn > Level2) Level2 = magn; #endif // Audio compression. dsample *= quisk_mic_clip; magn *= quisk_mic_clip; if (magn < Xmin) dsamples[i] = dsample; else if (magn > Xmax) dsamples[i] = copysign(Ymax, dsample); else dsamples[i] = copysign(aaa * magn * magn + bbb * magn + ccc, dsample); } } // remove clipping distortion; restrict bandwidth 300 to 2700 Hz count = quisk_dFilter(dsamples, count, &filtAudio2); if (is_ssb) { // SSB // FIR bandpass filter; separate into I and Q for (i = 0; i < count; i++) { csamples[i] = quisk_dC_out(dsamples[i], &filter2) * 2.0; // filter loss 0.5 #if DEBUG_IO || DEBUG magn = cabs(csamples[i]); if (magn > Level3) Level3 = magn; #endif } // round off peaks CcmPeak(NULL, csamples, count); #if DEBUG_IO || DEBUG for (i = 0; i < count; i++) { magn = cabs(csamples[i]); if (magn > Level4) Level4 = magn; } #endif // remove clipping distortion count = quisk_cDecimate(csamples, count, &cfiltAudio3, 1); // Interpolate up to 48000 if (MIC_OUT_RATE != sample_rate) count = quisk_cInterpolate(csamples, count, &cfiltInterp, MIC_OUT_RATE / sample_rate); // convert back to 16 bits for (i = 0; i < count; i++) { filtered[i] = csamples[i] * CLIP16; #if DEBUG_IO || DEBUG magn = cabs(csamples[i]); if (magn > dbOut) dbOut = magn; #endif } } else { // AM and FM #if DEBUG_IO || DEBUG for (i = 0; i < count; i++) { magn = fabs(dsamples[i]); if (magn > Level3) Level3 = magn; } #endif // round off peaks CcmPeak(dsamples, NULL, count); #if DEBUG_IO || DEBUG for (i = 0; i < count; i++) { magn = fabs(dsamples[i]); if (magn > Level4) Level4 = magn; } #endif // remove clipping distortion count = quisk_dFilter(dsamples, count, &dfiltAudio3); // Interpolate up to 48000 if (MIC_OUT_RATE != sample_rate) count = quisk_dInterpolate(dsamples, count, &dfiltInterp, MIC_OUT_RATE / sample_rate); // convert back to 16 bits for (i = 0; i < count; i++) { filtered[i] = dsamples[i] * CLIP16; #if DEBUG_IO || DEBUG magn = fabs(dsamples[i]); if (magn > dbOut) dbOut = magn; #endif } } #if DEBUG_IO || DEBUG if (debug_timer == 0) { if (dbOut > 1.0) clip = "Clip"; else clip = ""; dbOut = 20 * log10(dbOut); printf ("pre %3.1lf dB clip %2.0lf InMax %6.2lf Level0 %6.2lf Level1 %6.2lf Level2 %6.2lf Level3 %6.2lf Level4 %6.2lf dbOut %6.2lf %s\n", quisk_mic_preemphasis, 20 * log10(quisk_mic_clip), 20 * log10(inMax), 20 * log10(Level0), 20 * log10(Level1), 20 * log10(Level2), 20 * log10(Level3), 20 * log10(Level4), dbOut, clip); Level0 = Level1 = Level2 = Level3 = Level4 = dbOut = 0; } //QuiskPrintTime(" tx_filter", 2); #endif return count; } static int tx_filter_digital(complex double * filtered, int count) { // Input samples are creal(filtered), output is filtered. // This filter has minimal processing and is used for digital modes. int i; static int do_init = 1; static struct quisk_dFilter filter1; if (do_init) { // initialization do_init = 0; quisk_filt_dInit(&filter1, quiskDgtFilt48Coefs, sizeof(quiskDgtFilt48Coefs)/sizeof(double)); // pass 1350, stop 1650 } if ( ! filtered) { // Change to rxMode quisk_filt_tune(&filter1, 1650.0 / 48000, rxMode != DGT_L && rxMode != LSB); return 0; } for (i = 0; i < count; i++) // FIR bandpass filter; separate into I and Q filtered[i] = quisk_dC_out(creal(filtered[i]), &filter1) * 2.00; // tuned filter loss 0.5 //quisk_calc_audio_graph(CLIP16, filtered, NULL, count, 0); return count; } static int tx_filter_freedv(complex double * filtered, int count, int encode) { // Input samples are creal(filtered), output is filtered. // This filter is used for digital voice. int i; int sample_rate = 8000; double dtmp, magn, dsample; static int samples_size = 0; static int last_mode; static int do_init = 1; static double aaa, bbb, ccc, Xmin, Xmax, Ymax; static double time_long, time_short; static double inMax=0.3; static double * dsamples = NULL; static struct quisk_cFilter filter2; static struct quisk_dFilter filtDecim; static struct quisk_cFilter cfiltInterp; if (do_init) { // initialization //QuiskWavWriteOpen(&hWav, "jim.wav", 3, 1, 4, 8000, 1.0 / CLIP16); do_init = 0; quisk_filt_cInit(&filter2, quiskFilt53D2Coefs, sizeof(quiskFilt53D2Coefs)/sizeof(double)); // pass 1500, stop 1800 quisk_filt_tune((struct quisk_dFilter *)&filter2, 1600.0 / 8000, 1); // upper sideband last_mode = 11; quisk_filt_dInit(&filtDecim, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double)); // pass 3000, stop 4000 quisk_filt_cInit(&cfiltInterp, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double)); dtmp = 1.0 / sample_rate; // sample time time_long = 1.0 - exp(- dtmp / 3.000); time_short = 1.0 - exp(- dtmp / 0.005); Ymax = pow(10.0, - 1 / 20.0); // maximum y Xmax = pow(10.0, 3 / 20.0); // x where slope is zero; for x > Xmax, y == Ymax Xmin = Ymax - fabs(Ymax - Xmax); // x where slope is 1 and y = x; start of compression //printf ("Xmin %f\n", Xmin); aaa = 1.0 / (2.0 * (Xmin - Xmax)); // quadratic bbb = -2.0 * aaa * Xmax; ccc = Ymax - aaa * Xmax * Xmax - bbb * Xmax; } if (last_mode != rxMode) { // change to sideband if (rxMode == FDV_U) { // upper sideband last_mode = rxMode; quisk_filt_tune((struct quisk_dFilter *)&filter2, 1600.0 / 8000, 1); } else if (rxMode == FDV_L) { // lower sideband last_mode = rxMode; quisk_filt_tune((struct quisk_dFilter *)&filter2, 1600.0 / 8000, 0); } } if ( ! filtered) return 0; // check size of dsamples[] buffer if (count > samples_size) { samples_size = count * 2; if (dsamples) free(dsamples); dsamples = (double *)malloc(samples_size * sizeof(double)); } // copy to dsamples[] for (i = 0; i < count; i++) dsamples[i] = creal(filtered[i]) / CLIP16; // normalize to 1.0000 // Decimate to 8000 Hz if (quisk_sound_state.mic_sample_rate != sample_rate) count = quisk_dDecimate(dsamples, count, &filtDecim, quisk_sound_state.mic_sample_rate / sample_rate); // Measure average peak input audio level and limit for (i = 0; i < count; i++) { dsample = dsamples[i]; magn = fabs(dsample); if (magn > inMax) inMax = inMax * (1 - time_short) + time_short * magn; else if(magn > mic_agc_level) inMax = inMax * (1 - time_long) + time_long * magn; else inMax = inMax * (1 - time_long) + time_long * mic_agc_level; dsample = dsample / inMax * Xmin * 0.7; magn = fabs(dsample); if (magn < Xmin) dsamples[i] = dsample; else if (magn > Xmax) dsamples[i] = copysign(Ymax, dsample); else dsamples[i] = copysign(aaa * magn * magn + bbb * magn + ccc, dsample); dsamples[i] = dsamples[i] * CLIP16; } //QuiskWavWriteD(&hWav, dsamples, count); if (encode && pt_quisk_freedv_tx) // Encode audio into digital modulation count = (* pt_quisk_freedv_tx)(filtered, dsamples, count); //quisk_calc_audio_graph(CLIP16, filtered, NULL, count, 0); if (freedv_current_mode != FREEDV_MODE_700D) // 700D has its own filter count = quisk_cCDecimate(filtered, count, &filter2, 1); // Interpolate up to 48000 if (MIC_OUT_RATE != sample_rate) count = quisk_cInterpolate(filtered, count, &cfiltInterp, MIC_OUT_RATE / sample_rate); return count; } PyObject * quisk_get_tx_filter(PyObject * self, PyObject * args) { // return the TX filter response to display on the graph // This is for debugging. Change quisk.py to call QS.get_tx_filter() instead // of QS.get_filter(). int i, j, k; int freq, time; PyObject * tuple2; complex double cx; double scale; double * average, * fft_window, * bufI, * bufQ; fftw_complex * samples, * pt; // complex data for fft fftw_plan plan; // fft plan double phase, delta; int nTaps = 325; if (!PyArg_ParseTuple (args, "")) return NULL; // Create space for the fft of size data_width pt = samples = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * data_width); plan = fftw_plan_dft_1d(data_width, pt, pt, FFTW_FORWARD, FFTW_MEASURE); average = (double *) malloc(sizeof(double) * (data_width + nTaps)); fft_window = (double *) malloc(sizeof(double) * data_width); bufI = (double *) malloc(sizeof(double) * nTaps); bufQ = (double *) malloc(sizeof(double) * nTaps); for (i = 0, j = -data_width / 2; i < data_width; i++, j++) // Hanning fft_window[i] = 0.5 + 0.5 * cos(2. * M_PI * j / data_width); for (i = 0; i < data_width + nTaps; i++) average[i] = 0.5; // Value for freq == 0 for (freq = 1; freq < data_width / 2.0 - 10.0; freq++) { //freq = data_width * 0.2 / 48.0; delta = 2 * M_PI / data_width * freq; phase = 0; // generate some initial samples to fill the filter pipeline for (time = 0; time < data_width + nTaps; time++) { average[time] += cos(phase); // current sample phase += delta; if (phase > 2 * M_PI) phase -= 2 * M_PI; } } // now filter the signal using the transmit filter tx_filter(NULL, 0); // initialize scale = 1.0; for (i = 0; i < data_width; i++) if (fabs(average[i + nTaps]) > scale) scale = fabs(average[i + nTaps]); scale = CLIP16 / scale; // limit to CLIP16 for (i = 0; i < nTaps; i++) samples[i] = average[i] * scale; tx_filter(samples, nTaps); // process initial samples for (i = 0; i < data_width; i++) samples[i] = average[i + nTaps] * scale; tx_filter(samples, data_width); // process the samples for (i = 0; i < data_width; i++) // multiply by window samples[i] *= fft_window[i]; fftw_execute(plan); // Calculate FFT // Normalize and convert to log10 scale = 0.3 / data_width / scale; for (k = 0; k < data_width; k++) { cx = samples[k]; average[k] = cabs(cx) * scale; if (average[k] <= 1e-7) // limit to -140 dB average[k] = -7; else average[k] = log10(average[k]); } // Return the graph data tuple2 = PyTuple_New(data_width); i = 0; // Negative frequencies: for (k = data_width / 2; k < data_width; k++, i++) PyTuple_SetItem(tuple2, i, PyFloat_FromDouble(20.0 * average[k])); // Positive frequencies: for (k = 0; k < data_width / 2; k++, i++) PyTuple_SetItem(tuple2, i, PyFloat_FromDouble(20.0 * average[k])); free(bufQ); free(bufI); free(average); free(fft_window); fftw_destroy_plan(plan); fftw_free(samples); return tuple2; } // Send samples using the Metis-Hermes protocol. A frame is 8 bytes: L/R audio and I/Q mic samples. // All samples are 2 bytes. The 1032 byte UDP packet contains 63*2 radio sound samples, and 63*2 mic I/Q samples. // Samples are sent synchronously with the input samples. static void quisk_hermes_tx_reset(void) { // Reset the buffer to half full of zero samples int i; hermes_num_samples = HERMES_TX_BUF_SAMPLES / 2; hermes_read_index = 0; hermes_write_index = hermes_num_samples * 2; for (i = 0; i < HERMES_TX_BUF_SHORTS; i++) // Put zero mic samples into the buffer hermes_buf[i] = 0; //printf("quisk_hermes_tx_reset: hermes_num_samples %d\n", hermes_num_samples); } static void quisk_hermes_tx_add(complex double * cSamples, int tx_count, int key_down) { // Add samples to the Tx buffer. int i; static int hermes_buf_has_samples=1; if (key_down) { // add non-zero samples to buffer hermes_buf_has_samples = 1; } else if (hermes_buf_has_samples) { // key is not down; reset buffer to zero quisk_hermes_tx_reset(); hermes_buf_has_samples = 0; return; } else { // key is not down but buffer is zeroed; just reset pointers hermes_num_samples = HERMES_TX_BUF_SAMPLES / 2; hermes_read_index = 0; hermes_write_index = hermes_num_samples * 2; return; } //printf("hermes_tx_add start: hermes_num_samples %d, tx_count %d\n", hermes_num_samples, tx_count); if (hermes_num_samples + tx_count >= HERMES_TX_BUF_SAMPLES) { // no more space; throw away half the samples quisk_udp_mic_error("Tx hermes buffer overflow"); //printf("Tx hermes buffer overflow: hermes_num_samples %d tx_count %d\n", hermes_num_samples, tx_count); i = hermes_num_samples - HERMES_TX_BUF_SAMPLES / 2; // number of samples to remove hermes_num_samples -= i; hermes_read_index += i * 2; if (hermes_read_index >= HERMES_TX_BUF_SHORTS) hermes_read_index -= HERMES_TX_BUF_SHORTS; } hermes_num_samples += tx_count; for (i = 0; i < tx_count; i++) { // Put transmit mic samples into the buffer hermes_buf[hermes_write_index++] = (short)cimag(cSamples[i]); hermes_buf[hermes_write_index++] = (short)creal(cSamples[i]); if (hermes_write_index >= HERMES_TX_BUF_SHORTS) hermes_write_index = 0; } //printf ("Buffer usage %.1f %%, hermes_num_samples %d, tx_count %d\n", 100.0 * hermes_num_samples / HERMES_TX_BUF_SAMPLES, hermes_num_samples, tx_count); //printf("hermes_tx_add end: hermes_num_samples %d\n", hermes_num_samples); } void quisk_hermes_tx_send(int tx_socket, int * tx_records) { // Send one UDP block of mic samples using the Metis-Hermes protocol. Timing is from blocks received, rate is 48k. // If the key is up we send samples anyway, but the samples are zero. int i, offset, sent, ratio; short s; unsigned char sendbuf[1032]; unsigned char * pt_buf; static unsigned int seq = 0; static unsigned char C0_index = 0; static int mox_bit=0; // On key up, send some zero samples before releasing the T/R relay to Rx. static int mox_counter; // Timer for key-up delay. unsigned int hlwp = 0; //printf("hermes_tx_send start 1: hermes_num_samples %d\n", hermes_num_samples); if (tx_records == NULL) { seq = 0; C0_index = 0; quisk_hermes_tx_reset(); return; } if (quisk_is_key_down()) { mox_bit = 1; mox_counter = (int)((quiskKeyupDelay + 30) / 2.625 + 0.5); // 126 samples per block / 48000 == 2.625 msec } else { // Delay un-setting the MOX bit and continue sending zero-valued samples; when Tx signal is zero, unset MOX. if (mox_bit) { // This allows the Tx signal to go to zero before switching T/R relay. if (mox_counter > 0) mox_counter--; else mox_bit = 0; } } //printf("hermes_tx_send start 2: hermes_num_samples %d\n", hermes_num_samples); ratio = quisk_sound_state.sample_rate / 48000; // send rate is 48 ksps //printf ("quisk_hermes_tx_send ratio %d count %d\n", ratio, *tx_records); if (*tx_records / ratio < 63 * 2) // tx_records is the number of samples received for each receiver return; // Send 63*2 Tx samples with control bytes //printf ("Buffer usage %.1f %%\n", 100.0 * hermes_num_samples / HERMES_TX_BUF_SAMPLES); //printf ("Tx quisk_hermes_tx_send ratio %d, count %d, samples %d\n", ratio, *tx_records, hermes_num_samples); *tx_records -= 63 * 2 * ratio; if (hermes_num_samples < 63 * 2) { // Not enough samples to send //printf("Tx hermes buffer underflow: hermes_num_samples %d\n", hermes_num_samples); quisk_udp_mic_error("Tx hermes buffer underflow"); quisk_hermes_tx_reset(); } hermes_num_samples -= 63 * 2; sendbuf[0] = 0xEF; sendbuf[1] = 0xFE; sendbuf[2] = 0x01; sendbuf[3] = 0x02; sendbuf[4] = seq >> 24 & 0xFF; sendbuf[5] = seq >> 16 & 0xFF; sendbuf[6] = seq >> 8 & 0xFF; sendbuf[7] = seq & 0xFF; seq++; sendbuf[8] = 0x7F; sendbuf[9] = 0x7F; sendbuf[10] = 0x7F; offset = C0_index * 4; // offset into quisk_pc_to_hermes is C0[7:1] * 4 sendbuf[11] = C0_index << 1 | mox_bit; // C0 sendbuf[12] = quisk_pc_to_hermes[offset++]; // C1 sendbuf[13] = quisk_pc_to_hermes[offset++]; // C2 sendbuf[14] = quisk_pc_to_hermes[offset++]; // C3 sendbuf[15] = quisk_pc_to_hermes[offset++]; // C4 if (C0_index == 0) { // Do not change receiver count without stopping Hermes and restarting sendbuf[15] = quisk_multirx_count << 3 | 0x04; // Send the old count, not the changed count if (mox_bit) // send filter selection on J16 sendbuf[13] = hermes_filter_tx << 1; else sendbuf[13] = hermes_filter_rx << 1; } else if ( ! quisk_is_vna && C0_index == 9) { if (mox_bit) { // send Alex filter selection sendbuf[14] = alex_hpf_tx; sendbuf[15] = alex_lpf_tx; } else { sendbuf[14] = alex_hpf_rx; sendbuf[15] = alex_lpf_rx; } } if (++C0_index > 16) C0_index = 0; pt_buf = sendbuf + 16; for (i = 0; i < 63; i++) { // add 63 samples *pt_buf++ = 0x00; // Left/Right audio sample *pt_buf++ = 0x00; *pt_buf++ = 0x00; *pt_buf++ = 0x00; s = hermes_buf[hermes_read_index++]; *pt_buf++ = (s >> 8) & 0xFF; // Two bytes of I *pt_buf++ = s & 0xFF; s = hermes_buf[hermes_read_index++]; *pt_buf++ = (s >> 8) & 0xFF; // Two bytes of Q *pt_buf++ = s & 0xFF; if (hermes_read_index >= HERMES_TX_BUF_SHORTS) hermes_read_index = 0; } sendbuf[520] = 0x7F; sendbuf[521] = 0x7F; sendbuf[522] = 0x7F; // Changes for HermesLite v2 thanks to Steve, KF7O if ((quisk_hermeslite_writepointer > 0) && (quisk_hermeslite_writeattempts++ % 8 == 0)) { // Only send periodic hermeslite writes in second part of frame hlwp = 5*(quisk_hermeslite_writepointer-1); sendbuf[523] = quisk_hermeslite_writequeue[hlwp++] << 1 | mox_bit; sendbuf[524] = quisk_hermeslite_writequeue[hlwp++]; sendbuf[525] = quisk_hermeslite_writequeue[hlwp++]; sendbuf[526] = quisk_hermeslite_writequeue[hlwp++]; sendbuf[527] = quisk_hermeslite_writequeue[hlwp++]; if ((sendbuf[523] & 0x80) == 0) { // No acknowledge requested so fire and forget quisk_hermeslite_writepointer--; quisk_hermeslite_writeattempts = 0; } } else { offset = C0_index * 4; // offset into quisk_pc_to_hermes is C0[7:1] * 4 sendbuf[523] = C0_index << 1 | mox_bit; // C0 sendbuf[524] = quisk_pc_to_hermes[offset++]; // C1 sendbuf[525] = quisk_pc_to_hermes[offset++]; // C2 sendbuf[526] = quisk_pc_to_hermes[offset++]; // C3 sendbuf[527] = quisk_pc_to_hermes[offset++]; // C4 if (C0_index == 0) { sendbuf[527] = quisk_multirx_count << 3 | 0x04; // Send the old count, not the changed count if (mox_bit) // send filter selection on J16 sendbuf[525] = hermes_filter_tx << 1; else sendbuf[525] = hermes_filter_rx << 1; } else if ( ! quisk_is_vna && C0_index == 9) { if (mox_bit) { // send Alex filter selection sendbuf[526] = alex_hpf_tx; sendbuf[527] = alex_lpf_tx; } else { sendbuf[526] = alex_hpf_rx; sendbuf[527] = alex_lpf_rx; } } if (++C0_index > 16) C0_index = 0; if (quisk_hermeslite_writepointer > 0) quisk_hermeslite_writeattempts++; } // Abort after 53/8 ~= 5 retries if ((quisk_hermeslite_writepointer > 0) && (quisk_hermeslite_writeattempts > 53)) { printf("ERROR: Maximum Hermes-Lite write attempts\n"); // Cancel entire write sequence quisk_hermeslite_writepointer = 0; quisk_hermeslite_writeattempts = 0; } pt_buf = sendbuf + 528; for (i = 0; i < 63; i++) { // add 63 samples *pt_buf++ = 0x00; // Left/Right audio sample *pt_buf++ = 0x00; *pt_buf++ = 0x00; *pt_buf++ = 0x00; s = hermes_buf[hermes_read_index++]; *pt_buf++ = (s >> 8) & 0xFF; // Two bytes of I *pt_buf++ = s & 0xFF; s = hermes_buf[hermes_read_index++]; *pt_buf++ = (s >> 8) & 0xFF; // Two bytes of Q *pt_buf++ = s & 0xFF; if (hermes_read_index >= HERMES_TX_BUF_SHORTS) hermes_read_index = 0; } sent = send(tx_socket, (char *)sendbuf, 1032, 0); if (sent != 1032) quisk_udp_mic_error("Tx UDP socket error in Hermes"); //printf("hermes_tx_send end: hermes_num_samples %d\n", hermes_num_samples); } // udp_iq has an initial zero followed by the I/Q samples. // The initial zero is sent iff align4 == 1. static void transmit_udp(complex double * cSamples, int count) { // Send count samples using the HiQSDR protocol. Each sample is sent as two shorts (4 bytes) of I/Q data. // Transmission is delayed until a whole block of data is available. int i, sent; static short udp_iq[TX_BLOCK_SHORTS + 1] = {0}; static int udp_size = 1; if (mic_socket == INVALID_SOCKET) return; if ( ! cSamples) { // initialization udp_size = 1; udp_iq[0] = 0; // should not be necessary return; } if (doTxCorrect) { for (i = 0; i < count; i++) cSamples[i] = cSamples[i] * TxCorrectLevel + TxCorrectDc; } for (i = 0; i < count; i++) { // transmit samples udp_iq[udp_size++] = (short)creal(cSamples[i]); udp_iq[udp_size++] = (short)cimag(cSamples[i]); if (udp_size >= TX_BLOCK_SHORTS) { // check count if (align4) sent = send(mic_socket, (char *)udp_iq, udp_size * 2, 0); else sent = send(mic_socket, (char *)udp_iq + 1, --udp_size * 2, 0); if (sent != udp_size * 2) printf("Send socket returned %d\n", sent); udp_size = 1; } } } static void transmit_mic_carrier(complex double * cSamples, int count, double level) { // send a CW carrier instead of mic samples #if 1 // transmit a carrier equal to the number of samples int i; for (i = 0; i < count; i++) cSamples[i] = level * CLIP16; #else // replace mic samples with a sin wave int i; double freq; complex double phase1; // Phase increment static complex double vector1 = CLIP16 / 2; // Use the sidetone slider 0 to 1000 to set frequency freq = quisk_sidetoneCtrl * 5; freq = ((int)freq / 50) * 50; phase1 = cexp(I * 2.0 * M_PI * freq / 48000.0); //phase1 = conj(phase1); for (i = 0; i < count; i++) { vector1 *= phase1; cSamples[i] = vector1 * level; } #endif } static void transmit_mic_imd(complex double * cSamples, int count, double level) { // send a 2-tone test signal instead of mic samples int i; complex double v; static complex double phase1=0, phase2; // Phase increment static complex double vector1; static complex double vector2; if (phase1 == 0) { // initialize phase1 = cexp((I * 2.0 * M_PI * IMD_TONE_1) / MIC_OUT_RATE); phase2 = cexp((I * 2.0 * M_PI * IMD_TONE_2) / MIC_OUT_RATE); vector1 = CLIP16 / 2.0; vector2 = CLIP16 / 2.0; } for (i = 0; i < count; i++) { // transmit a carrier equal to the number of samples vector1 *= phase1; vector2 *= phase2; v = level * (vector1 + vector2); cSamples[i] = v; } } int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int count) { int i, sample, maximum, interp, mic_interp, key_down; double d, ctcss_delta, audio_scale, ctcss_scale; static int key_was_down=0, key_down_counter=0; static struct quisk_cFilter filtInterp={NULL}; static struct quisk_cFilter filtInterp2={NULL}; static struct quisk_cFilter filt240D4 = {NULL}; static struct quisk_cFilter filt300D6 = {NULL}; static struct quisk_cHB45Filter HalfBand = {NULL, 0, 0}; static double ctcss_angle=0; static struct alc tx_alc = {NULL}; #if 0 // Measure soundcard actual sample rate static time_t seconds = 0; static int total = 0; struct timeval tb; static double dtime; gettimeofday(&tb); total += count; if (seconds == 0) { seconds = tb.tv_sec; dtime = tb.tv_sec + 0.000001 * tb.tv_usec; } else if (tb.tv_sec - seconds > 4) { printf("Mic soundcard rate %.3f\n", total / (tb.tv_sec + .000001 * tb.tv_usec - dtime)); seconds = tb.tv_sec; printf("backlog %d, count %d\n", backlog, count); } #endif #if DEBUG_IO || DEBUG //QuiskPrintTime("", -1); #endif #if DEBUG_IO || DEBUG double magn; static double out_max=0; #endif #if DEBUG_LEVEL || DEBUG_IO || DEBUG debug_timer += count; if (debug_timer >= mic_sample_rate) // one second debug_timer = 0; #endif // Microphone sample are input at mic_sample_rate. But after processing, // the output rate is MIC_OUT_RATE. interp = MIC_OUT_RATE / mic_sample_rate; #if USE_GET_SIN get_sin(cSamples, count); // Replace mic samples with a sin wave #endif #if USE_2TONE get_2tone(cSamples, count); // Replace mic samples with a 2-tone test signal #endif // measure maximum microphone level maximum = 1; for (i = 0; i < count; i++) { cSamples[i] *= (double)CLIP16 / CLIP32; // convert 32-bit samples to 16 bits d = creal(cSamples[i]); sample = (int)fabs(d); if (sample > maximum) maximum = sample; } // VOX processing if (maximum > vox_level) { is_vox = mic_sample_rate / 1000 * timeVOX; // reset timer to maximum } else if(is_vox) { is_vox -= count; // decrement timer if (is_vox < 0) is_vox = 0; } // mic display level if (maximum > mic_level) mic_level = maximum; mic_timer -= count; // time out the max microphone level to display if (mic_timer <= 0) { mic_timer = mic_sample_rate / 1000 * MIC_MAX_HOLD_TIME; mic_max_display = mic_level; mic_level = 1; } if ( ! tx_alc.buffer) init_alc(&tx_alc, 960); // at 48000 sps, 960 is 20 msec // quiskTxHoldState is a state machine to implement a pause for a repeater frequency shift for FM key_down = quisk_is_key_down(); if (rxMode == FM || rxMode == DGT_FM) { switch (quiskTxHoldState) { case 0: // Never implement any hold break; case 1: // Start hold when key goes down if (key_down) quiskTxHoldState = 2; break; case 2: // Key down hold is in progress; wait until state changes to 3 break; case 3: // Hold is released; when key goes up, hold starts again if ( ! key_down) quiskTxHoldState = 4; break; case 4: // Key up hold is in progress; wait until state changes to 1 break; } } if (quiskTxHoldState == 2 || quiskTxHoldState == 4) { // don't transmit until the hold is cleared key_down = 0; for (i = 0; i < count; i++) cSamples[i] = 0; } if (key_down != key_was_down) { key_down_counter = 4800; // Key was pressed. Zero the first few samples to clear the buffers. init_alc(&tx_alc, 0); // init ALC for Tx and RECORD_MIC key_was_down = key_down; } if (key_down || DEBUG_MIC) { // create transmit I/Q samples #if USE_GET_SIN == 2 transmit_udp(cSamples, count * interp); #else if (quiskSpotLevel >= 0) { // Spot is in use count *= interp; transmit_mic_carrier(cSamples, count, quiskSpotLevel / 1000.0); } else switch (rxMode) { case LSB: // LSB case USB: // USB if (quisk_record_state == PLAYBACK) count = tx_filter_digital(cSamples, count); // filter samples, minimal processing else count = tx_filter(cSamples, count); // filter samples process_alc(cSamples, count, &tx_alc, rxMode); break; case AM: // AM if (quisk_record_state != PLAYBACK) // no audio processing for recorded sound count = tx_filter(cSamples, count); for (i = 0; i < count; i++) // transmit (0.5 + ampl/2, 0) cSamples[i] = (creal(cSamples[i]) + CLIP16) * 0.5; process_alc(cSamples, count, &tx_alc, rxMode); break; case FM: // FM if (quisk_record_state != PLAYBACK) // no audio processing for recorded sound count = tx_filter(cSamples, count); if (quisk_ctcss_freq > 9) { ctcss_delta = 2.0 * M_PI / MIC_OUT_RATE * quisk_ctcss_freq; ctcss_scale = 450.0 * modulation_index / quisk_ctcss_freq; // for 15% of total deviation audio_scale = 0.85 * modulation_index / CLIP16; for (i = 0; i < count; i++) { ctcss_angle += ctcss_delta; if (ctcss_angle >= 2.0 * M_PI) ctcss_angle -= 2.0 * M_PI; cSamples[i] = CLIP16 * cexp(I * (audio_scale * creal(cSamples[i]) + ctcss_scale * sin (ctcss_angle))); } } else { audio_scale = modulation_index / CLIP16; for (i = 0; i < count; i++) // this is phase modulation == FM and 6 dB /octave preemphasis cSamples[i] = CLIP16 * cexp(I * audio_scale * creal(cSamples[i])); } process_alc(cSamples, count, &tx_alc, rxMode); break; case DGT_U: // external digital modes case DGT_L: case DGT_IQ: case DGT_FM: count = tx_filter_digital(cSamples, count); // filter samples, minimal processing process_alc(cSamples, count, &tx_alc, rxMode); break; case IMD: // transmit IMD 2-tone test count *= interp; transmit_mic_imd(cSamples, count, quiskImdLevel / 1000.0); break; case FDV_U: // FDV case FDV_L: count = tx_filter_freedv(cSamples, count, 1); process_alc(cSamples, count, &tx_alc, rxMode); break; default: break; } #if DEBUG_IO || DEBUG for (i = 0; i < count; i++) { magn = cabs(cSamples[i]); if (out_max < magn) out_max = magn; } if (debug_timer == 0) { printf("Max cSamples[] Output Level %9.6f\n", out_max / CLIP16); out_max = 0; } #endif if (key_down_counter > 0) { // zero the first few samples to clear the buffers. for (i = 0; i < count && key_down_counter > 0; i++, key_down_counter--) { if (key_down_counter < 480) cSamples[i] *= (1.0 - key_down_counter / 480.0); // slow increase at end else cSamples[i] = 0; // initial samples are zero } } #endif } if (reverse_tx_sideband) for (i = 0; i < count; i++) cSamples[i] = conj(cSamples[i]); if(quisk_pt_sample_write) { // Used for SoapySDR // Interpolate the mic samples to the Tx sample rate //printf("Tx sample rate %i\n", tx_sample_rate); switch (tx_sample_rate) { case 100000: count = quisk_cInterp2HB45(cSamples, count, &HalfBand); // Fall through case 50000: if (! filt240D4.dCoefs) quisk_filt_cInit(&filt240D4, quiskFilt240D4Coefs, sizeof(quiskFilt240D4Coefs)/sizeof(double)); if (! filt300D6.dCoefs) quisk_filt_cInit(&filt300D6, quiskFilt300D6Coefs, sizeof(quiskFilt300D6Coefs)/sizeof(double)); count = quisk_cInterpDecim(cSamples, count, &filt240D4, 5, 4); // 60 kSps count = quisk_cInterpDecim(cSamples, count, &filt300D6, 5, 6); // 50 kSps break; case 96000: if (! filtInterp2.dCoefs) quisk_filt_cInit(&filtInterp2, quiskFilt48dec24Coefs, sizeof(quiskFilt48dec24Coefs)/sizeof(double)); count = quisk_cInterpolate(cSamples, count, &filtInterp2, 2); break; case 192000: if (! filtInterp2.dCoefs) quisk_filt_cInit(&filtInterp2, quiskFilt48dec24Coefs, sizeof(quiskFilt48dec24Coefs)/sizeof(double)); count = quisk_cInterp2HB45(cSamples, count, &HalfBand); count = quisk_cInterpolate(cSamples, count, &filtInterp2, 2); break; default: // 48000 break; } (*quisk_pt_sample_write)(cSamples, count); } else if (quisk_use_rx_udp == 10) { // Send Hermes mic samples when key is up or down if ( ! quisk_rx_udp_started) ; else { if ((rxMode == CWL || rxMode == CWU) && quiskSpotLevel < 0) // CW and no Spot for (i = 0; i < count; i++) cSamples[i] = 0; quisk_hermes_tx_add(cSamples, count, key_down); } } else if (quisk_use_rx_udp && key_down) { // Send mic samples to UDP when key is down transmit_udp(cSamples, count); } if (quisk_record_state == RECORD_MIC) { switch (rxMode) { case LSB: // LSB case USB: // USB count = tx_filter(cSamples, count); // filter samples process_alc(cSamples, count, &tx_alc, rxMode); break; case AM: // AM count = tx_filter(cSamples, count); process_alc(cSamples, count, &tx_alc, rxMode); break; case FM: // FM count = tx_filter(cSamples, count); process_alc(cSamples, count, &tx_alc, rxMode); break; case FDV_U: // FDV case FDV_L: count = tx_filter_freedv(cSamples, count, 0); process_alc(cSamples, count, &tx_alc, rxMode); break; default: for (i = 0; i < count; i++) cSamples[i] = 0; break; } // Perhaps interpolate the mic samples back to the sound play rate mic_interp = quisk_sound_state.playback_rate / MIC_OUT_RATE; if (mic_interp > 1) { if (! filtInterp.dCoefs) quisk_filt_cInit(&filtInterp, quiskFilt12_19Coefs, sizeof(quiskFilt12_19Coefs)/sizeof(double)); count = quisk_cInterpolate(cSamples, count, &filtInterp, mic_interp); } quisk_tmp_record(cSamples, count, (double)CLIP32 / CLIP16); // convert 16 to 32 bits } #if DEBUG_IO || DEBUG //QuiskPrintTime(" process_mic", 1); #endif return count; } PyObject * quisk_set_tx_audio(PyObject * self, PyObject * args, PyObject * keywds) { /* Call with keyword arguments ONLY; change Tx audio parameters */ static char * kwlist[] = {"vox_level", "vox_time", "mic_clip", "mic_preemphasis", "tx_sample_rate", "reverse_tx_sideband", NULL} ; int vlevel = -9999, clevel = -9999; if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiidii", kwlist, &vlevel, &timeVOX, &clevel, &quisk_mic_preemphasis, &tx_sample_rate, &reverse_tx_sideband)) return NULL; if (vlevel != -9999) vox_level = (int)(pow(10.0, vlevel / 20.0) * CLIP16); // Convert dB to 16-bit sample if (clevel != -9999) quisk_mic_clip = pow(10.0, clevel / 20.0); // Convert dB to factor Py_INCREF (Py_None); return Py_None; } PyObject * quisk_set_udp_tx_correct(PyObject * self, PyObject * args) // Called from GUI thread { double DcI, DcQ, level; if (!PyArg_ParseTuple (args, "ddd", &DcI, &DcQ, &level)) return NULL; if (DcI == 0 && DcQ == 0 && level == 1.0){ doTxCorrect = 0; } else { doTxCorrect = 1; TxCorrectDc = (DcI + I * DcQ) * CLIP16; DcI = fabs(DcI); DcQ = fabs(DcQ); if (DcI > DcQ) TxCorrectLevel = 1.0 - DcI; else TxCorrectLevel = 1.0 - DcQ; TxCorrectLevel *= level; } Py_INCREF (Py_None); return Py_None; } PyObject * quisk_is_vox(PyObject * self, PyObject * args) { /* return the VOX state */ if (!PyArg_ParseTuple (args, "")) return NULL; return PyInt_FromLong(is_vox); } PyObject * quisk_set_hermes_filter(PyObject * self, PyObject * args) { if (!PyArg_ParseTuple (args, "ii", &hermes_filter_rx, &hermes_filter_tx)) return NULL; Py_INCREF (Py_None); return Py_None; } PyObject * quisk_set_alex_hpf(PyObject * self, PyObject * args) { if (!PyArg_ParseTuple (args, "ii", &alex_hpf_rx, &alex_hpf_tx)) return NULL; Py_INCREF (Py_None); return Py_None; } PyObject * quisk_set_alex_lpf(PyObject * self, PyObject * args) { if (!PyArg_ParseTuple (args, "ii", &alex_lpf_rx, &alex_lpf_tx)) return NULL; Py_INCREF (Py_None); return Py_None; } void quisk_close_mic(void) { if (mic_socket != INVALID_SOCKET) { close(mic_socket); mic_socket = INVALID_SOCKET; } #ifdef MS_WINDOWS if (mic_cleanup) WSACleanup(); #endif } void quisk_open_mic(void) { struct sockaddr_in Addr; int sndsize = 48000; #if DEBUG_IO || DEBUG int intbuf; #ifdef MS_WINDOWS int bufsize = sizeof(int); #else socklen_t bufsize = sizeof(int); #endif #endif #ifdef MS_WINDOWS WORD wVersionRequested; WSADATA wsaData; #endif modulation_index = QuiskGetConfigDouble("modulation_index", 1.6); mic_agc_level = QuiskGetConfigDouble("mic_agc_level", 0.1); if (quisk_sound_state.tx_audio_port == 0x553B) align4 = 0; // Using old port: data starts at byte 42. else align4 = 1; // Start data at byte 44; align to dword if (quisk_sound_state.mic_ip[0]) { #ifdef MS_WINDOWS wVersionRequested = MAKEWORD(2, 2); if (WSAStartup(wVersionRequested, &wsaData) != 0) return; // failure to start winsock mic_cleanup = 1; #endif mic_socket = socket(PF_INET, SOCK_DGRAM, 0); if (mic_socket != INVALID_SOCKET) { setsockopt(mic_socket, SOL_SOCKET, SO_SNDBUF, (char *)&sndsize, sizeof(sndsize)); Addr.sin_family = AF_INET; // This is the UDP port for TX microphone samples, and must agree with the microcontroller. Addr.sin_port = htons(quisk_sound_state.tx_audio_port); #ifdef MS_WINDOWS Addr.sin_addr.S_un.S_addr = inet_addr(quisk_sound_state.mic_ip); #else inet_aton(quisk_sound_state.mic_ip, &Addr.sin_addr); #endif if (connect(mic_socket, (const struct sockaddr *)&Addr, sizeof(Addr)) != 0) { close(mic_socket); mic_socket = INVALID_SOCKET; } else { #if DEBUG_IO || DEBUG if (getsockopt(mic_socket, SOL_SOCKET, SO_SNDBUF, (char *)&intbuf, &bufsize) == 0) printf("UDP mic socket send buffer size %d\n", intbuf); else printf ("Failure SO_SNDBUF\n"); #endif } } } } void quisk_set_tx_mode(void) // called when the mode rxMode is changed { tx_filter(NULL, 0); tx_filter_digital(NULL, 0); transmit_udp(NULL, 0); tx_filter_freedv(NULL, 0, 0); }