static GtkWidget *cws;
static GtkWidget *b_enable_cws;
static GtkWidget *b_enable_cwlr;
+static GtkWidget *b_cw_active_low;
#endif
static gboolean save_cb (GtkWidget *widget, GdkEventButton *event, gpointer data) {
FUNCTION_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(function));
#ifdef LOCALCW
ENABLE_CW_BUTTONS=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_cwlr))?1:0;
+ CW_ACTIVE_LOW=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_cw_active_low))?1:0;
CWL_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cwl));
CWR_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cwr));
ENABLE_GPIO_SIDETONE=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_cws))?1:0;
gtk_widget_show(cwl);
gtk_grid_attach(GTK_GRID(grid),cwl,4,y,1,1);
- b_enable_cwlr=gtk_check_button_new_with_label("Enable CW buttons");
+ b_enable_cwlr=gtk_check_button_new_with_label("CWLR Enable");
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_enable_cwlr), ENABLE_CW_BUTTONS);
gtk_widget_show(b_enable_cwlr);
gtk_grid_attach(GTK_GRID(grid),b_enable_cwlr,5,y,1,1);
gtk_spin_button_set_value (GTK_SPIN_BUTTON(cwr),CWR_BUTTON);
gtk_widget_show(cwr);
gtk_grid_attach(GTK_GRID(grid),cwr,4,y,1,1);
+
+ b_cw_active_low=gtk_check_button_new_with_label("CWLR active-low");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_cw_active_low), CW_ACTIVE_LOW);
+ gtk_widget_show(b_cw_active_low);
+ gtk_grid_attach(GTK_GRID(grid),b_cw_active_low,5,y,1,1);
#endif
y++;
gtk_container_add(GTK_CONTAINER(content),grid);
-/*
- GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Save",GTK_RESPONSE_OK);
- //gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20"));
-*/
gtk_widget_show_all(dialog);
int result=gtk_dialog_run(GTK_DIALOG(dialog));
-/*
- ENABLE_VFO_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_vfo_encoder))?1:0;
- VFO_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(vfo_a));
- VFO_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(vfo_b));
- ENABLE_VFO_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_vfo_pullup))?1:0;
- ENABLE_E1_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E1_encoder))?1:0;
- E1_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E1_a));
- E1_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E1_b));
- ENABLE_E1_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E1_pullup))?1:0;
- ENABLE_E2_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E2_encoder))?1:0;
- E2_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E2_a));
- E2_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(E2_b));
- ENABLE_E2_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_E2_pullup))?1:0;
- ENABLE_E3_ENCODER=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S6_encoder))?1:0;
- E3_ENCODER_A=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S6_a));
- E3_ENCODER_B=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S6_b));
- ENABLE_E3_PULLUP=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S6_pullup))?1:0;
- ENABLE_S1_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S1))?1:0;
- S1_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S1));
- ENABLE_S2_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S2))?1:0;
- S2_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S2));
- ENABLE_S3_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S3))?1:0;
- S3_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S3));
- ENABLE_S4_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S4))?1:0;
- S4_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S4));
- ENABLE_S5_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S5))?1:0;
- S5_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S5));
- ENABLE_S6_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_S6))?1:0;
- S6_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(S6));
- ENABLE_MOX_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_mox))?1:0;
- MOX_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(mox));
- ENABLE_FUNCTION_BUTTON=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(b_enable_function))?1:0;
- FUNCTION_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(function));
-#ifdef LOCALCW
- CWL_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cwl));
- CWR_BUTTON=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(cwr));
-#endif
-*/
-
- gtk_widget_destroy(dialog);
-
-// gpio_save_state();
-
}
#endif
cw_changed();
}
-static void cw_active_level_cb(GtkWidget *widget, gpointer data) {
- cw_active_level=cw_active_level==1?0:1;
- cw_changed();
-}
-
static void cw_keyer_speed_value_changed_cb(GtkWidget *widget, gpointer data) {
cw_keyer_speed=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
cw_changed();
gtk_widget_show(cw_keyer_internal_b);
gtk_grid_attach(GTK_GRID(grid),cw_keyer_internal_b,0,10,1,1);
g_signal_connect(cw_keyer_internal_b,"toggled",G_CALLBACK(cw_keyer_internal_cb),NULL);
-
- GtkWidget *cw_active_level_b=gtk_check_button_new_with_label("CW Local Active_Low");
- //gtk_widget_override_font(cw_active_level_b, pango_font_description_from_string("Arial 18"));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_active_level_b), cw_active_level==0);
- gtk_widget_show(cw_active_level_b);
- gtk_grid_attach(GTK_GRID(grid),cw_active_level_b,1,10,1,1);
- g_signal_connect(cw_active_level_b,"toggled",G_CALLBACK(cw_active_level_cb),NULL);
#endif
gtk_container_add(GTK_CONTAINER(content),grid);
// The following calls functions can be called usig g_idle_add
-// DL1YCF: added interface for mode change, to be used by rigctl
-// (MD command)
int ext_vfo_mode_changed(void * data)
{
- vfo_mode_changed((int) (long) data);
+ int mode=(uintptr_t) data;
+ vfo_mode_changed(mode);
return 0;
}
int ext_set_frequency(void *data) {
setFrequency(*(long long *)data);
free(data);
- // DL1YCF added return statement
- // this one is CRITICAL to avoid free() being called
- // repeatedly on the same pointer
return 0;
}
return 0;
}
-// DL1YCF: because of the new CW algorithm,
-// this function is no longer used
-int ext_cw_setup() {
- radio_cw_setup();
- return 0;
-}
-
-// DL1YCF: because of the new CW algorithm,
-// this function is no longer used
-int ext_cw_key(void *data) {
- radio_cw_key((uintptr_t)data);
- return 0;
-}
-
int ext_update_squelch(void *data) {
set_squelch(active_receiver);
return 0;
#ifdef PURESIGNAL
int ext_tx_set_ps(void *data) {
- tx_set_ps(transmitter,(uintptr_t)data);
+ int state=(uintptr_t) data;
+ tx_set_ps(transmitter, state);
return 0;
}
#endif
int SIDETONE_GPIO=8;
int ENABLE_GPIO_SIDETONE=0;
int ENABLE_CW_BUTTONS=1;
+int CW_ACTIVE_LOW=1;
#endif
static volatile int vfoEncoderPos;
#ifdef LOCALCW
value=getProperty("ENABLE_CW_BUTTONS");
if(value) ENABLE_CW_BUTTONS=atoi(value);
+ value=getProperty("CW_ACTIVE_LOW");
+ if(value) CW_ACTIVE_LOW=atoi(value);
value=getProperty("CWL_BUTTON");
if(value) CWL_BUTTON=atoi(value);
value=getProperty("CWR_BUTTON");
#ifdef LOCALCW
sprintf(value,"%d",ENABLE_CW_BUTTONS);
setProperty("ENABLE_CW_BUTTONS",value);
+ sprintf(value,"%d",CW_ACTIVE_LOW);
+ setProperty("CW_ACTIVE_LOW",value);
sprintf(value,"%d",CWL_BUTTON);
setProperty("CWL_BUTTON",value);
sprintf(value,"%d",CWR_BUTTON);
#ifdef LOCALCW
-// Note we cannot use debouncing, as after the first interrupt,
-// we might read the wrong level. So we process all interrupts
-// to the keyer.
-// The only way to do proper debouncing is to record the wall-clock time
-// of the last state change of each of the two buttons, and
-// disable further state changes for a short time (5 msec)
+//
+// We generate interrupts only on falling edge
+//
static void setup_cw_pin(int pin, void(*pAlert)(void)) {
fprintf(stderr,"setup_cw_pin: pin=%d \n",pin);
if (cw_keyer_internal != 0) return; // as quickly as possible
level=digitalRead(CWL_BUTTON);
//fprintf(stderr,"cwl button : level=%d \n",level);
- keyer_event(CWL_BUTTON, cw_active_level == 0 ? level : (level==0));
+ //the second parameter of keyer_event ("state") is TRUE on key-down
+ keyer_event(CWL_BUTTON, CW_ACTIVE_LOW ? (level==0) : level);
}
static void cwAlert_right() {
if (cw_keyer_internal != 0) return; // as quickly as possible
level=digitalRead(CWR_BUTTON);
//fprintf(stderr,"cwr button : level=%d \n",level);
- keyer_event(CWR_BUTTON, cw_active_level == 0 ? level : (level==0));
+ keyer_event(CWR_BUTTON, CW_ACTIVE_LOW ? (level==0) : level);
}
+//
+// Two functions added, might be useful somewhere
+//
+int gpio_left_cw_key() {
+ int val=digitalRead(CWL_BUTTON);
+ return CW_ACTIVE_LOW? (val==0) : val;
+}
+
+int gpio_right_cw_key() {
+ int val=digitalRead(CWR_BUTTON);
+ return CW_ACTIVE_LOW? (val==0) : val;
+}
#endif
int gpio_init() {
extern int SIDETONE_GPIO;
extern int ENABLE_GPIO_SIDETONE;
extern int ENABLE_CW_BUTTONS;
+extern int CW_ACTIVE_LOW;
void gpio_sidetone(int freq);
+int gpio_left_cw_key();
+int gpio_right_cw_key();
#endif
void gpio_restore_state();
}
#ifdef GPIO
-void keyer_event(int gpio, int level) {
- int state = (level == 0);
-
+void keyer_event(int gpio, int state) {
if (state) {
// This is for aborting CAT CW messages if the key is hit.
cw_key_hit = 1;
// PTT has been engaged manually
if (running && !cwvox && !mox) {
g_idle_add(ext_mox_update, (gpointer)(long) 1);
- cwvox=(int) vox_hang;
+ cwvox=(int) cw_breakin;
}
}
if (gpio == CWL_BUTTON)
// If MOX still hanging, continue spinnning/checking and decrement cwvox
while (key_state != EXITLOOP || cwvox > 0) {
- if (cwvox > 0 && key_state != EXITLOOP && key_state != CHECK) cwvox=(int) vox_hang;
+ if (cwvox > 0 && key_state != EXITLOOP && key_state != CHECK) cwvox=(int) cw_breakin;
switch (key_state) {
case EXITLOOP:
if (cwvox >0) cwvox--;
FILE* f=fopen(filename,"w+");
char line[512];
char version[32];
+
+ fprintf(stderr,"saveProperties: %s\n",filename);
+
if(!f) {
fprintf(stderr,"can't open %s\n",filename);
return;
int cw_keyer_hang_time=300; // ms
int cw_keyer_sidetone_frequency=400; // Hz
int cw_breakin=1; // 0=disabled 1=enabled
-int cw_active_level=1; // 0=active_low 1=active_high
int vfo_encoder_divisor=15;
value=getProperty("cw_keyer_internal");
if(value) cw_keyer_internal=atoi(value);
#endif
- value=getProperty("cw_active_level");
- if(value) cw_active_level=atoi(value);
value=getProperty("cw_keyer_sidetone_volume");
if(value) cw_keyer_sidetone_volume=atoi(value);
value=getProperty("cw_keyer_ptt_delay");
setProperty("cw_keyer_spacing",value);
sprintf(value,"%d",cw_keyer_internal);
setProperty("cw_keyer_internal",value);
- sprintf(value,"%d",cw_active_level);
- setProperty("cw_active_level",value);
sprintf(value,"%d",cw_keyer_sidetone_volume);
setProperty("cw_keyer_sidetone_volume",value);
sprintf(value,"%d",cw_keyer_ptt_delay);
extern int cw_keyer_hang_time;
extern int cw_keyer_sidetone_frequency;
extern int cw_breakin;
-extern int cw_active_level;
extern int vfo_encoder_divisor;
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) {
case '7': strcpy(pattern,"--...");break;
case '8': strcpy(pattern,"---..");break;
case '9': strcpy(pattern,"----.");break;
- case '.': strcpy(pattern,".-.-.-");break;
- case '/': strcpy(pattern,"-..-.");break;
- case ',': strcpy(pattern,"--..--");break;
- case '!': strcpy(pattern,"-.-.--");break;
- case ')': strcpy(pattern,"-.--.-");break;
- case '(': strcpy(pattern,"-.--.-");break;
+//
+// DL1YCF:
+// There were some signs I considered wrong, other
+// signs missing. Therefore I put the signs here
+// from ITU Recommendation M.1677-1 (2009)
+// in the order given there.
+//
+ case '.': strcpy(pattern,".-.-.-"); break;
+ case ',': strcpy(pattern,"--..--"); break;
+ case ':': strcpy(pattern,"---.."); break;
+ case '?': strcpy(pattern,"..--.."); break;
+ case '\'': strcpy(pattern,".----."); break;
+ case '-': strcpy(pattern,"-....-"); break;
+ case '/': strcpy(pattern,"-..-."); break;
+ case '(': strcpy(pattern,"-.--."); break;
+ case ')': strcpy(pattern,"-.--.-"); break;
+ case '"': strcpy(pattern,".-..-."); break;
+ case '=': strcpy(pattern,"-...-"); break;
+ case '+': strcpy(pattern,".-.-."); break;
+ case '@': strcpy(pattern,".--.-."); break;
+//
+// Often used, but not ITU: Ampersand for "wait"
+//
case '&': strcpy(pattern,".-...");break;
- case ':': strcpy(pattern,"---..");break;
- case '+': strcpy(pattern,".-.-.");break;
- case '-': strcpy(pattern,"-....-");break;
- case '_': strcpy(pattern,".--.-.");break;
- case '@': strcpy(pattern,"..--.-");break;
- default: strcpy(pattern," ");
+ default: strcpy(pattern,"");
}
while(*ptr != '\0') {
}
ptr++;
}
- // 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.
+
+ // The last element (dash or dot) sent already has one dotlen space appended.
+ // If the current character is another "printable" sign, we need an additional
+ // pause of 2 dotlens to form the inter-character spacing of 3 dotlens.
+ // However if the current character is a "space" we must produce an inter-word
+ // spacing (7 dotlens) and therefore need 6 additional dotlens
+ // We need no longer take care of a sequence of spaces since adjacent spaces
+ // are now filtered out while filling the CW character (ring-) buffer.
+
if (cw_char == ' ') {
- if (last_cw_char != ' ') send_space(6);
+ send_space(6); // produce inter-word space of 7 dotlens
} else {
- send_space(2);
+ send_space(2); // produce inter-character space of 3 dotlens
}
- last_cw_char=cw_char;
}
//
// sending each word with a separate KY command
// produces perfectly readable CW.
//
+// UPDATE: we maintain a ring buffer such that
+// the contents of several KY commands
+// can be buffered
+//
static gpointer rigctl_cw_thread(gpointer data)
{
int i;
char c;
- char local_buf[30];
+ char last_char=0;
+ char ring_buf[130];
+ char *write_buf=ring_buf;
+ char *read_buf =ring_buf;
+ char *p;
+ int num_buf=0;
while (server_running) {
// wait for cw_buf become filled with data
// (periodically look every 100 msec)
- cw_key_hit=0;
- if (!cw_busy) {
+ if (!cw_busy && num_buf ==0) {
+ cw_key_hit=0;
usleep(100000L);
continue;
}
- strncpy(local_buf, cw_buf, 30);
- cw_busy=0; // mark buffer free again
+
+ // if new data available and space in buffer, copy it
+ // If there are several adjacent spaces, take only the first one.
+ // This also swallows the "tails" of the KY commands which
+ // (according to Kenwood) have to be padded with spaces up
+ // to the maximum length (24)
+
+ if (cw_busy && num_buf < 100) {
+ p=cw_buf;
+ while ((c=*p++)) {
+ if (last_char == ' ' && c == ' ') continue;
+ *write_buf++ = c;
+ last_char=c;
+ num_buf++;
+ if (write_buf - ring_buf == 128) write_buf=ring_buf; // wrap around
+ }
+ cw_busy=0; // mark buffer free again
+ }
+
// these values may have changed, so recompute them here
+ // This means that we can change the speed (KS command) while
+ // the buffer is being sent
+
dotlen = 1200000L/(long)cw_keyer_speed;
dashlen = (dotlen * 3 * cw_keyer_weight) / 50L;
dotsamples = 57600 / cw_keyer_speed;
// forever here, so allow at most 500 msec
i=10;
while (!mox && (i--) > 0) usleep(50000L);
- // still no MOX? --> silently discard CW message and give up
+ // still no MOX? --> silently discard CW character and give up
if (!mox) {
CAT_cw_is_active=0;
continue;
usleep(100000L);
}
// At this point, mox==1 and CAT_cw_active == 1
- i=0;
- while(((c=local_buf[i++]) != '\0') && !cw_key_hit && !cw_not_ready) {
- rigctl_send_cw_char(c);
- }
if (cw_key_hit || cw_not_ready) {
//
// CW transmission has been aborted, either due to manually
// CAT CW commands. We need this long time since
// hamlib waits 0.5 secs after receiving a "KY1" message before trying to
// send the next bunch
- for (i=0; i< 50; i++) {
- cw_busy=0; // mark buffer free
- usleep(20000L);
- }
+ cw_busy=-1; // mark buffer purge situation
+ usleep(1000000L);
+ cw_busy=0;
+ write_buf=read_buf=ring_buf;
+ num_buf=0;
} else {
+ rigctl_send_cw_char(*read_buf++);
+ if (read_buf - ring_buf == 128) read_buf=ring_buf; // wrap around
+ num_buf--;
//
- // CAT CW message has been sent.
- // If the next message is pending, continue.
- // Otherwise remove PTT and wait for next CAT
- // CW command.
- if (cw_busy) continue;
+ // Character has been sent.
+ // If there are more to send, or the next message is pending, continue.
+ // Otherwise remove PTT and wait for next CAT CW command.
+ if (cw_busy || num_buf > 0) continue;
CAT_cw_is_active=0;
g_idle_add(ext_ptt_update ,(gpointer)0);
+ // wait up to 500 msec for MOX having gone
+ // otherwise there might be a race condition when sending
+ // the next character really soon
+ i=10;
+ while (mox && (i--) > 0) usleep(50000L);
}
}
// We arrive here if the rigctl server shuts down.
// of a transmission
rigctl_cw_thread_id = NULL;
cw_busy=0;
+ CAT_cw_is_active=0;
g_idle_add(ext_ptt_update ,(gpointer)0);
return NULL;
}
// - if we can accept new data (buffer space available) : "KY0;"
// - if buffer is full: "KY1;"
//
+ // Note: cw_buse == -1 indicates a "purge KY" situation, where
+ // all KY commands are accepted and data is discared silently
+ // In this case cw_busy is left untouched here.
if (len <= 2) {
- if (cw_busy) {
+ if (cw_busy == 1) {
send_resp(client_sock,"KY1;");
} else {
send_resp(client_sock,"KY0;");
// "busy" is correctly queried.
// - Note further that the space immediately following "KY" is *not*
// part of the message.
- if (!cw_busy && len > 3) {
+ if ((cw_busy==0) && (len > 3)) {
// A CW text will be sent. Copy incoming data to buffer
strncpy(cw_buf, cmd_input+3, 29);
// Kenwood protocol allows for at most 24 characters, so
cw_buf[29]=0;
cw_busy=1;
}
+ // cwbusy == -1 or empty text: do nothing
}
}
else if(strcmp(cmd_str,"LK")==0) {