From: c vw Date: Tue, 3 Jul 2018 17:11:28 +0000 (+0200) Subject: CAT CW now works, and many other fixes connect with local X-Git-Url: https://git.rkrishnan.org/listings/...?a=commitdiff_plain;h=40928b291cdeae3c839be4696dddeea41f16bf08;p=pihpsdr.git CAT CW now works, and many other fixes connect with local CW generation, CW tx frequencies, and CW/TUNE side tones. --- diff --git a/old_protocol.c b/old_protocol.c index 8b2aa12..6ff6c18 100644 --- a/old_protocol.c +++ b/old_protocol.c @@ -691,6 +691,30 @@ void old_protocol_audio_samples(RECEIVER *rx,short left_audio_sample,short right } } +// +// This is a copy of old_protocol_iq_samples, +// but it includes the possibility to send a side tone +// We use it to provide a side-tone for CW/TUNE, in +// all other cases side==0 and this routine then is +// fully equivalent to old_protocol_iq_samples. +// +void old_protocol_iq_samples_with_sidetone(int isample, int qsample, int side) { + if(isTransmitting()) { + output_buffer[output_buffer_index++]=side >> 8; + output_buffer[output_buffer_index++]=side; + output_buffer[output_buffer_index++]=side >> 8; + output_buffer[output_buffer_index++]=side; + output_buffer[output_buffer_index++]=isample>>8; + output_buffer[output_buffer_index++]=isample; + output_buffer[output_buffer_index++]=qsample>>8; + output_buffer[output_buffer_index++]=qsample; + if(output_buffer_index>=OZY_BUFFER_SIZE) { + ozy_send_buffer(); + output_buffer_index=8; + } + } +} + void old_protocol_iq_samples(int isample,int qsample) { if(isTransmitting()) { output_buffer[output_buffer_index++]=0; @@ -1201,7 +1225,13 @@ void ozy_send_buffer() { mode=vfo[0].mode; } if(mode==modeCWU || mode==modeCWL) { - if(tune) { +// +// VOX is set in CW if we send +// morse code via CAT. This is +// supported even if we use the +// external keyer for our paddles. +// + if(tune || vox) { output_buffer[C0]|=0x01; } } else { diff --git a/old_protocol.h b/old_protocol.h index f179090..c7cf63c 100644 --- a/old_protocol.h +++ b/old_protocol.h @@ -29,4 +29,5 @@ extern void old_protocol_set_mic_sample_rate(int rate); extern void old_protocol_process_local_mic(unsigned char *buffer,int le); extern void old_protocol_audio_samples(RECEIVER *rx,short left_audio_sample,short right_audio_sample); extern void old_protocol_iq_samples(int isample,int qsample); +extern void old_protocol_iq_samples_with_sidetone(int isample,int qsample,int side); #endif diff --git a/portaudio.c b/portaudio.c index 69f77fc..dfb1dc0 100644 --- a/portaudio.c +++ b/portaudio.c @@ -367,11 +367,14 @@ void audio_close_output(RECEIVER *rx) { // we have to store the data such that the PA callback function // can access it. // -static int apt=0; int audio_write (RECEIVER *rx, short r, short l) { PaError err; + // this will cause massive underflow errors, since + // we do not provide any data while transmitting. + // Instead, the side tone version will take over + if (rx == active_receiver && isTransmitting()) return 0; if (rx->playback_handle != NULL && rx->playback_buffer != NULL) { rx->playback_buffer[rx->playback_offset++] = (r + l) *0.000015259; // 65536 --> 1.0 if (rx->playback_offset == audio_buffer_size) { @@ -386,11 +389,22 @@ int audio_write (RECEIVER *rx, short r, short l) return 0; } -// -// CW audio write -// This is a dummy here because I think it is not correctly implemented in audio.c -// -void cw_audio_write(double sample) { -} +int cw_audio_write(double sample) { + PaError err; + RECEIVER *rx = active_receiver; + + if (rx->playback_handle != NULL && rx->playback_buffer != NULL) { + rx->playback_buffer[rx->playback_offset++] = sample; + if (rx->playback_offset == audio_buffer_size) { + rx->playback_offset=0; + err=Pa_WriteStream(rx->playback_handle, (void *) rx->playback_buffer, (unsigned long) audio_buffer_size); + //if (err != paNoError) { + // fprintf(stderr,"PORTAUDIO ERROR: write stream dev=%d: %s\n",out_device_no[rx->audio_device],Pa_GetErrorText(err)); + // return -1; + // } + } + } + return 0; +} #endif diff --git a/radio.c b/radio.c index 121fddf..a2235ee 100644 --- a/radio.c +++ b/radio.c @@ -844,14 +844,22 @@ void setTune(int state) { } pre_tune_mode=mode; + // DL1YCF: in USB/DIGU/DSB, tune 1000 Hz above carrier + // in LSB/DIGL, tune 1000 Hz below carrier + // all other (CW, AM, FM): tune on carrier freq. + // Note: do not look at CW sidetone freq here. switch(mode) { case modeLSB: - case modeCWL: case modeDIGL: - SetTXAPostGenToneFreq(transmitter->id,-(double)cw_keyer_sidetone_frequency); + SetTXAPostGenToneFreq(transmitter->id,-(double)1000.0); + break; + case modeUSB: + case modeDSB: + case modeDIGU: + SetTXAPostGenToneFreq(transmitter->id,(double)1000.0); break; default: - SetTXAPostGenToneFreq(transmitter->id,(double)cw_keyer_sidetone_frequency); + SetTXAPostGenToneFreq(transmitter->id,(double)0.0); break; } @@ -895,15 +903,9 @@ void radio_cw_setup() { mode=vfo[VFO_B].mode; } - double freq=(double)cw_keyer_sidetone_frequency; - switch(mode) { - case modeCWU: - SetTXAPostGenToneFreq(transmitter->id,(double)cw_keyer_sidetone_frequency); - break; - case modeLSB: - SetTXAPostGenToneFreq(transmitter->id,-(double)cw_keyer_sidetone_frequency); - break; - } + // DL1YCF: to be "transceive" in CW, our signal + // needs to be spot-on the nominal frequency + SetTXAPostGenToneFreq(transmitter->id,(double) 0.0); SetTXAPostGenMode(transmitter->id,0); SetTXAPostGenToneMag(transmitter->id,0.99999); } diff --git a/rigctl.c b/rigctl.c index 06a841b..76af632 100644 --- a/rigctl.c +++ b/rigctl.c @@ -54,11 +54,13 @@ #include "rigctl_menu.h" #include +extern void vox_trigger(int lead_in); +extern void vox_untrigger(); + // IP stuff below #include #include //inet_addr -#undef RIGCTL_DEBUG //#define RIGCTL_DEBUG int rigctl_port_base=19090; @@ -76,8 +78,6 @@ static const int TelnetPortC = 19092; #define MAXDATASIZE 2000 void parse_cmd (); -int cw_busy = 0; // Used to signal that the system is in a busy state for sending CW - assert busy back to the user -int cw_reset = 0; // Signals reset the transceiver int connect_cnt = 0; int rigctlGetFilterLow(); @@ -111,6 +111,7 @@ FILTER * band_filter; #define MAX_CLIENTS 3 static GThread *rigctl_server_thread_id = NULL; static GThread *rigctl_set_timer_thread_id = NULL; +static GThread *rigctl_cw_thread_id = NULL; static int server_running; static GThread *serial_server_thread_id = NULL; @@ -293,35 +294,74 @@ int convert_ctcss() { } int vfo_sm=0; // VFO State Machine - this keeps track of -// Now my stuff +// +// CW sending stuff +// + +static char cw_buf[30]; +static int cw_busy=0; + +static long dotlen; +static long dashlen; +static int dotsamples; +static int dashsamples; + +extern int cw_key_up, cw_key_down; + +// +// send_dash() send a "key-down" of a dashlen, followed by a "key-up" of a dotlen +// send_dot() send a "key-down" of a dotlen, followed by a "key-up" of a dotlen +// send_space(int len) send a "key_down" of zero, followed by a "key-up" of len*dotlen +// +// The "trick" to get proper timing is, that we really specify the number of samples +// for the next element (dash/dot/nothing) and the following pause. 30 wpm is no +// problem, and without too much "busy waiting". We just take a nap until 10 msec +// before we have to act, and then wait several times for 1 msec until we can shoot. // void send_dash() { - //long delay = (1200000L * ((long)cw_keyer_weight/10L))/(long)cw_keyer_speed ; - int dot_delay = 1200/cw_keyer_speed; - int delay = (dot_delay * 3 * cw_keyer_weight)/50; - g_idle_add(ext_cw_key, (gpointer)(long)1); - usleep((long)delay*3000L); - g_idle_add(ext_cw_key, (gpointer)(long)0); - usleep((long)delay * 1000L); - //fprintf(stderr,"_%d",mox); - + int TimeToGo; + for(;;) { + TimeToGo=cw_key_up+cw_key_down; + if (TimeToGo == 0) break; + // sleep until 10 msec before ignition + if (TimeToGo > 500) usleep((long)(TimeToGo-500)*20L); + // sleep 1 msec + usleep(1000L); + } + cw_key_down = dashsamples; + cw_key_up = dotsamples; } + void send_dot() { - int dot_delay = 1200/cw_keyer_speed; - g_idle_add(ext_cw_key, (gpointer)(long)1); - usleep((long)dot_delay * 1000L); - g_idle_add(ext_cw_key, (gpointer)(long)0); - usleep((long)dot_delay* 1000L); - //fprintf(stderr,".%d",mox); + int TimeToGo; + for(;;) { + TimeToGo=cw_key_up+cw_key_down; + if (TimeToGo == 0) break; + // sleep until 10 msec before ignition + if (TimeToGo > 500) usleep((long)(TimeToGo-500)*20L); + // sleep 1 msec + usleep(1000L); + } + cw_key_down = dotsamples; + cw_key_up = dotsamples; } -void send_space() { - int dot_delay = 1200/cw_keyer_speed; - usleep((long)dot_delay* 7000L); - //fprintf(stderr," %d",mox); + +void send_space(int len) { + int TimeToGo; + for(;;) { + TimeToGo=cw_key_up+cw_key_down; + if (TimeToGo == 0) break; + // sleep until 10 msec before ignition + if (TimeToGo > 500) usleep((long)(TimeToGo-500)*20L); + // sleep 1 msec + usleep(1000L); + } + cw_key_up = len*dotsamples; } void rigctl_send_cw_char(char cw_char) { char pattern[9],*ptr; + static char last_cw_char=0; strcpy(pattern,""); ptr = &pattern[0]; switch (cw_char) { @@ -358,7 +398,7 @@ void rigctl_send_cw_char(char cw_char) { case 'p': case 'P': strcpy(pattern,".--."); break; case 'q': - case 'Q': strcpy(pattern,"-.--"); break; + case 'Q': strcpy(pattern,"--.-"); break; case 'r': case 'R': strcpy(pattern,".-."); break; case 's': @@ -397,11 +437,8 @@ void rigctl_send_cw_char(char cw_char) { case '-': strcpy(pattern,"-....-");break; case '_': strcpy(pattern,".--.-.");break; case '@': strcpy(pattern,"..--.-");break; - case ' ': strcpy(pattern," ");break; default: strcpy(pattern," "); } - //fprintf(stderr,"Sending %c:",cw_char); - g_idle_add(ext_cw_key, (gpointer)(long)0); while(*ptr != '\0') { if(*ptr == '-') { @@ -410,16 +447,66 @@ void rigctl_send_cw_char(char cw_char) { if(*ptr == '.') { send_dot(); } - if(*ptr == ' ') { - send_space(); - } ptr++; } - // Need a delay HERE between characters - long delay = (1200000L * ((long)cw_keyer_weight/10L))/(long)cw_keyer_speed ; - usleep(delay*3L); - //fprintf(stderr,"\n"); + // The last character sent already has one dotlen included. + // Therefore, if the character was a "space", we need an additional + // inter-word pause of 6 dotlen, else we need a inter-character + // pause of 2 dotlens. + // Note that two or more adjacent space characters result in a + // single inter-word distance. This also gets rid of trailing + // spaces in the KY command. + if (cw_char == ' ') { + if (last_cw_char != ' ') send_space(6); + } else { + send_space(2); + } + last_cw_char=cw_char; } + +// +// This thread constantly looks whether CW data +// is available, and produces CW in this case. +// The buffer is copied to a local buffer and +// immediately released, such that the next +// chunk can already be prepeared. This way, +// splitting a large CW text into words, and +// sending each word with a separate KY command +// produces perfectly readable CW. +// +static gpointer rigctl_cw_thread(gpointer data) +{ + int index; + char c; + char local_buf[30]; + + while (server_running) { + // wait for cw_buf become filled with data + if (!cw_busy) { + usleep(100000L); + continue; + } + dotlen = 1200000L/(long)cw_keyer_speed; + dashlen = (dotlen * 3 * cw_keyer_weight) / 50L; + dotsamples = 57600 / cw_keyer_speed; + dashsamples = (3456 * cw_keyer_weight) / cw_keyer_speed; + strncpy(local_buf, cw_buf, 30); + index=0; + cw_busy=0; // mark buffer free again + // We need a relatively long lead-in time after triggering VOX + // Otherwise the first element is shortened + vox_trigger(100); // lead-in 100 msec, only before the first character is sent. + while(((c=local_buf[index++]) != '\0')) { + rigctl_send_cw_char(c); + } + vox_untrigger(); + } + // We arrive here if the rigctl server shuts down. + rigctl_cw_thread_id = NULL; + cw_busy=0; + return NULL; +} + void gui_vfo_move_to(gpointer data) { long long freq = *(long long *) data; fprintf(stderr,"GUI: %11lld\n",freq); @@ -486,6 +573,10 @@ static gpointer rigctl_server(gpointer data) { client[i].socket=-1; } server_running=1; + + // must start the thread here in order NOT to inherit a lock + if (!rigctl_cw_thread_id) rigctl_cw_thread_id = g_thread_new("RIGCTL cw", rigctl_cw_thread, NULL); + while(server_running) { // listen with a max queue of 3 if(listen(server_socket,3)<0) { @@ -588,41 +679,6 @@ static gpointer rigctl_client (gpointer data) { if(save_flag != 1) { work_ptr = strtok(cmd_input,";"); while(work_ptr != NULL) { - if(cw_busy == 1) { - if(strlen(work_ptr)>2) { - cw_check_buf[0] = work_ptr[0]; - cw_check_buf[1] = work_ptr[1]; - cw_check_buf[2] = '\0'; - if(strcmp("ZZ",cw_check_buf)==0) { - if(strlen(work_ptr)>4) { - cw_check_buf[0] = work_ptr[2]; - cw_check_buf[1] = work_ptr[3]; - cw_check_buf[2] = '\0'; - } else { - send_resp(client->socket,"?;"); - } - } - } else { - // Illegal Command - send_resp(client->socket,"?;"); - } - // Got here because we have a legal command in cw_check_buf - // Look for RESET and BUSY which re respond to else - send ?; - if(strcmp("BY",cw_check_buf)==0) { - send_resp(client->socket,"BY11;"); // Indicate that we are BUSY - } else if (strcmp("SR",cw_check_buf) == 0) { - // Reset the transceiver - g_mutex_lock(&mutex_c->m); - cw_reset = 1; - g_mutex_unlock(&mutex_c->m); - // Wait till BUSY clears - while(cw_busy); - g_mutex_lock(&mutex_c->m); - cw_reset = 0; - g_mutex_unlock(&mutex_c->m); - } - - } // Lock so only one user goes into this at a time g_mutex_lock(&mutex_b->m); parse_cmd(work_ptr,strlen(work_ptr),client->socket); @@ -2048,38 +2104,40 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { } } } - else if((strcmp(cmd_str,"KY")==0) && (zzid_flag == 0)) { - // TS-2000 - KY - Convert char to morse code - not supported - int index = 2; - long delay = 1200000L/(long)cw_keyer_speed; // uS - #ifdef RIGCTL_DEBUG - fprintf(stderr,"RIGCTL: KY DELAY=%ld, cw_keyer_speed=%d cw_keyer_weight=%d\n",delay,cw_keyer_speed,cw_keyer_weight); - #endif - if(len <=2) { - send_resp(client_sock,"KY0;"); - } else { - // Set CW BUSY flag - g_mutex_lock(&mutex_c->m); - cw_busy = 1; - g_mutex_unlock(&mutex_c->m); - while((cmd_input[index] != '\0') && (!cw_reset)) { - //fprintf(stderr,"send=%c\n",cmd_input[index]); - rigctl_send_cw_char(cmd_input[index]); - //fprintf(stderr,"RIGCTL: 0 mox=%d\n",mox); - index++; - } - #ifdef RIGCTL_DEBUG - fprintf(stderr,"RIGCTL: KY - Done sending cw\n"); - fprintf(stderr,"RIGCTL: 1 mox=%d\n",mox); - #endif - } - g_mutex_lock(&mutex_c->m); - cw_busy = 0; - g_mutex_unlock(&mutex_c->m); - #ifdef RIGCTL_DEBUG - fprintf(stderr,"RIGCTL: 2 mox=%d\n",mox); - #endif - } + else if((strcmp(cmd_str,"KY")==0) && (zzid_flag == 0)) + { + // DL1YCF: + // Hamlib produces errors if we keep begin busy here for + // seconds. Therefore we just copy the data to be handled + // by a separate thread. + // Note that this thread only makes a 0 --> 1 transition for cw_busy, + // and the CW thread only makes a 1 --> 0 transition + // + // Note: for a "KY;" command, we have to return "KY0;" if we can + // accept new data (buffer space available) and "KY1;" if we cannot, + // + if (len <= 2) { + if (cw_busy) { + send_resp(client_sock,"KY1;"); // can store no more data + } else { + send_resp(client_sock,"KY0;"); + } + } else { + // So we have data. We have to init the CW setup because TUNE + // changes the WDSP side tone frequency. + g_idle_add(ext_cw_setup,NULL); // Initialize for external transmit + // We silently ignore buffer overruns. This does not happen with + // hamlib since I fixed it. + if (!cw_busy) { + // Copy data to buffer + strncpy(cw_buf, cmd_input+2, 30); + // Kenwood protocol allows for at most 28 characters, so + // this is pure paranoia + cw_buf[29]=0; + cw_busy=1; + } + } + } else if(strcmp(cmd_str,"LK")==0) { // TS-2000 - LK - Set/read key lock function status // PiHPSDR - ZZLK - Set/read key lock function status diff --git a/transmitter.c b/transmitter.c index aeac250..454ea0c 100644 --- a/transmitter.c +++ b/transmitter.c @@ -51,6 +51,7 @@ #include "ext.h" double getNextSideToneSample(); +double getNextInternalSideToneSample(); #define min(x,y) (xout_of_band=0; @@ -559,6 +563,8 @@ fprintf(stderr,"transmitter: allocate buffers: mic_input_buffer=%d iq_output_buf tx->iq_output_buffer=malloc(sizeof(double)*2*tx->output_samples); tx->samples=0; tx->pixel_samples=malloc(sizeof(float)*tx->pixels); + if (cw_shape_buffer) free(cw_shape_buffer); + cw_shape_buffer=malloc(sizeof(double)*tx->buffer_size); fprintf(stderr,"transmitter: allocate buffers: mic_input_buffer=%p iq_output_buffer=%p pixels=%p\n",tx->mic_input_buffer,tx->iq_output_buffer,tx->pixel_samples); fprintf(stderr,"create_transmitter: OpenChannel id=%d buffer_size=%d fft_size=%d sample_rate=%d dspRate=%d outputRate=%d\n", @@ -719,10 +725,11 @@ void tx_set_pre_emphasize(TRANSMITTER *tx,int state) { static void full_tx_buffer(TRANSMITTER *tx) { long isample; long qsample; - double gain; + double gain,lgain,sidevol; int j; int error; - int mode; + int mode,do_shape; + int sidetone=0; switch(protocol) { case ORIGINAL_PROTOCOL: @@ -733,6 +740,17 @@ static void full_tx_buffer(TRANSMITTER *tx) { break; } + // do_shape means that we "shape" the signal with the + // envelope in cw_shape_buf, with values between 0 and 200 + // this is the only way to produce click-free CW signals. + mode=vfo[VFO_A].mode; + if(split) { + mode=vfo[VFO_B].mode; + } + do_shape= (mode == modeCWL || mode == modeCWU) && !tune; + if (do_shape) sidevol= 1.29 * cw_keyer_sidetone_volume; // will be multiplied with cw_shape + if (tune) sidevol= 258.0 * cw_keyer_sidetone_volume; + update_vox(tx); fexchange0(tx->id, tx->mic_input_buffer, tx->iq_output_buffer, &error); @@ -758,14 +776,23 @@ static void full_tx_buffer(TRANSMITTER *tx) { } } + if (do_shape) gain=gain*0.005; for(j=0;joutput_samples;j++) { + lgain=gain; + if (do_shape) lgain=gain*cw_shape_buffer[j]; double is=tx->iq_output_buffer[j*2]; double qs=tx->iq_output_buffer[(j*2)+1]; - isample=is>=0.0?(long)floor(is*gain+0.5):(long)ceil(is*gain-0.5); - qsample=qs>=0.0?(long)floor(qs*gain+0.5):(long)ceil(qs*gain-0.5); + isample=is>=0.0?(long)floor(is*lgain+0.5):(long)ceil(is*lgain-0.5); + qsample=qs>=0.0?(long)floor(qs*lgain+0.5):(long)ceil(qs*lgain-0.5); switch(protocol) { case ORIGINAL_PROTOCOL: - old_protocol_iq_samples(isample,qsample); + // produce a side tone for internal CW and tune on the HPSDR board + // since we may have used getNextSidetoneSample above we need + // an independent instance thereof here. To be nice to the CW + // operator, the audio is shaped the same way as the RF + if (tune) sidetone=sidevol * getNextInternalSideToneSample(); + if (do_shape) sidetone=sidevol * cw_shape_buffer[j] * getNextInternalSideToneSample(); + old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone); break; case NEW_PROTOCOL: new_protocol_iq_samples(isample,qsample); @@ -788,20 +815,38 @@ void add_mic_sample(TRANSMITTER *tx,short mic_sample) { mode=vfo[0].mode; } - if (tune) { - mic_sample_double=0.0; + // This is valid if not CW and not tuning + mic_sample_double=(double)mic_sample/32768.0; + + if (tune) mic_sample_double=0.0; + + if((mode==modeCWL || mode==modeCWU)) { + mic_sample_double=0.0; + if (isTransmitting()) { +// +// The CW part sets the variables cw_key_up and cw_key_down +// to the number of samples for the next down/up sequence. +// cw_key_down can be zero, for inserting some space +// +// We HAVE TO shape the signal to avoid hard clicks to be +// heard way beside our frequency. The envelope goes up +// and down linearly within 200 samples (4.16 msec) +// + if (cw_key_down > 0 ) { + if (cw_shape < 200) cw_shape++; + cw_key_down--; + } else { + if (cw_key_up >= 0) { + // dig into this even if cw_key_up is already zero, to ensure + // that cw_shape eventually reaches zero + if (cw_shape > 0) cw_shape--; + if (cw_key_up > 0) cw_key_up--; + } } - else if(mode==modeCWL || mode==modeCWU) { - if (isTransmitting()) { - if (key == 1) { - mic_sample_double = getNextSideToneSample(); - cw_audio_write(mic_sample_double * cw_keyer_sidetone_volume/ 127.0); - mic_sample_double = mic_sample_double * 200000; //* amplitude - } else mic_sample_double=0.0; - } - } else { - mic_sample_double=(double)mic_sample/32768.0; + cw_audio_write(0.00003937 * getNextSideToneSample() * cw_keyer_sidetone_volume * cw_shape); + } } + cw_shape_buffer[tx->samples]=cw_shape; tx->mic_input_buffer[tx->samples*2]=mic_sample_double; tx->mic_input_buffer[(tx->samples*2)+1]=0.0; //mic_sample_double; tx->samples++; @@ -920,23 +965,36 @@ void tx_set_ps_sample_rate(TRANSMITTER *tx,int rate) { } #endif -void cw_sidetone_mute(int mute){ - key = mute; +// +// This is the old key-down/key-up interface for iambic.c +// but now it also smoothes (cw_shape) the signal +// +void cw_sidetone_mute(int mute) { + if (mute) { + cw_key_down = 480000; // up to 10 sec, should be OK even for QRSS + } else { + cw_key_down = 0; + } } -int asteps = 0; -double timebase = 0.0; -#define TIMESTEP (1.0 / 48000) -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif +// DL1YCF: +// somewhat improved, and provided two siblings +// for generating side tones simultaneously on the +// HPSDR board and local audio. + +#define TWOPIOVERSAMPLERATE 0.0001308996938995747; // 2 Pi / 48000 + +static long asteps = 0; +static long bsteps = 0; + double getNextSideToneSample() { - double angle = cw_keyer_sidetone_frequency * 2 * M_PI * timebase; - timebase += TIMESTEP; - asteps++; - if (asteps == 48000) { - timebase = 0.0; - asteps = 0; - } + double angle = (asteps*cw_keyer_sidetone_frequency)*TWOPIOVERSAMPLERATE; + if (++asteps == 48000) asteps = 0; + return sin(angle); +} + +double getNextInternalSideToneSample() { + double angle = (bsteps*cw_keyer_sidetone_frequency)*TWOPIOVERSAMPLERATE; + if (++bsteps == 48000) bsteps = 0; return sin(angle); } diff --git a/vox.c b/vox.c index 5b9cbb2..9058b7d 100644 --- a/vox.c +++ b/vox.c @@ -26,8 +26,10 @@ #include "ext.h" static guint vox_timeout; +static guint trigger_timeout = -1; static double peak=0.0; +static int cwvox=0; static int vox_timeout_cb(gpointer data) { //setVox(0); @@ -36,6 +38,15 @@ static int vox_timeout_cb(gpointer data) { return FALSE; } +static int cwvox_timeout_cb(gpointer data) { + cwvox=0; + trigger_timeout=-1; + g_idle_add(ext_vox_changed,(gpointer)0); + g_idle_add(ext_cw_key ,(gpointer)0); + g_idle_add(ext_vfo_update,NULL); + return FALSE; +} + double vox_get_peak() { double result=peak; @@ -49,6 +60,9 @@ void update_vox(TRANSMITTER *tx) { int i; double sample; peak=0.0; + + if (cwvox) return; // do not set peak and fiddle around with VOX when in local CW mode + for(i=0;ibuffer_size;i++) { sample=tx->mic_input_buffer[i*2]; if(sample<0.0) { @@ -74,6 +88,29 @@ void update_vox(TRANSMITTER *tx) { } } +// vox_trigger activates MOX, vox_enabled does not matter here +// if VOX this is already pending, +void vox_trigger(int lead_in) { + + // delete any pending hang timers + if (trigger_timeout >= 0) g_source_remove(trigger_timeout); + + if (!cwvox) { + cwvox=1; + g_idle_add(ext_vox_changed,(gpointer)1); + g_idle_add(ext_cw_key ,(gpointer)1); + g_idle_add(ext_vfo_update,NULL); + usleep((long)lead_in*1000L); // lead-in time is in ms + } +} + +// vox_untrigger actives the vox hang timer for cwvox +// so an immediately following "KY" CAT command will +// just proceed. +void vox_untrigger() { + trigger_timeout=g_timeout_add((int)vox_hang,cwvox_timeout_cb,NULL); +} + void vox_cancel() { if(vox) { g_source_remove(vox_timeout);