return oldval;
}
+//
+// This interface puts an "action" into the GTK idle queue,
+// but CW actions are processed immediately
+//
+void schedule_action(enum ACTION action, enum ACTION_MODE mode, gint val) {
+ PROCESS_ACTION *a;
+ switch (action) {
+ case CW_LEFT:
+ case CW_RIGHT:
+#ifdef LOCALCW
+ keyer_event(action==CW_LEFT,val);
+#else
+ g_print("CW_Left/Right but compiled without LOCALCW\n");
+#endif
+ break;
+ case CW_KEYER:
+ //
+ // hard "key-up/down" action WITHOUT break-in
+ // intended for external keyers (MIDI or GPIO connected)
+ // which take care of PTT themselves
+ //
+ if (val != 0 && cw_keyer_internal == 0) {
+ cw_key_down=960000; // max. 20 sec to protect hardware
+ cw_key_up=0;
+ cw_key_hit=1;
+ } else {
+ cw_key_down=0;
+ cw_key_up=0;
+ }
+ break;
+ default:
+ //
+ // schedule action through GTK idle queue
+ //
+ a=g_new(PROCESS_ACTION, 1);
+ a->action=action;
+ a->mode=mode;
+ a->val=val;
+ g_idle_add(process_action, a);
+ break;
+ }
+}
+
int process_action(void *data) {
PROCESS_ACTION *a=(PROCESS_ACTION *)data;
double value;
if(active_receiver->agc>+AGC_LAST) {
active_receiver->agc=0;
}
- set_agc(active_receiver);
+ set_agc(active_receiver, active_receiver->agc);
g_idle_add(ext_vfo_update, NULL);
}
break;
break;
case SPLIT:
if(a->mode==PRESSED) {
- if(can_transmit) {
- set_split(split==1?0:1);
- g_idle_add(ext_vfo_update, NULL);
- }
+ g_idle_add(ext_split_toggle, NULL);
}
break;
case SQUELCH:
extern ACTION_TABLE ActionTable[ACTIONS+1];
extern int process_action(void *data);
+extern void schedule_action(enum ACTION action, enum ACTION_MODE mode, gint val);
send_agc(client_socket,active_receiver->id,active_receiver->agc);
} else {
#endif
- set_agc(active_receiver);
+ set_agc(active_receiver, active_receiver->agc);
g_idle_add(ext_vfo_update, NULL);
#ifdef CLIENT_SERVER
}
}
static void rx_ant_cb(GtkToggleButton *widget, gpointer data) {
- //
- // Note this function is only called for the ORIGINAL and NEW protocol
- //
if(gtk_toggle_button_get_active(widget)) {
int b=(GPOINTER_TO_UINT(data))>>4;
int ant=(GPOINTER_TO_UINT(data))&0xF;
BAND *band=band_get_band(b);
band->alexRxAntenna=ant;
- set_alex_rx_antenna();
+ radio_band_changed();
}
}
static void adc0_antenna_cb(GtkComboBox *widget,gpointer data) {
- //
- // Note this function is only called for the SOAPYSDR protocol
- //
ADC *adc=(ADC *)data;
adc->antenna=gtk_combo_box_get_active(widget);
if(radio->protocol==NEW_PROTOCOL) {
}
static void dac0_antenna_cb(GtkComboBox *widget,gpointer data) {
- //
- // Note this function is only called for the SOAPYSDR protocol
- //
DAC *dac=(DAC *)data;
dac->antenna=gtk_combo_box_get_active(widget);
if(radio->protocol==NEW_PROTOCOL) {
}
static void tx_ant_cb(GtkToggleButton *widget, gpointer data) {
- //
- // Note this function is only called for the ORIGINAL and NEW protocol
- //
if(gtk_toggle_button_get_active(widget)) {
int b=(GPOINTER_TO_UINT(data))>>4;
int ant=(GPOINTER_TO_UINT(data))&0xF;
BAND *band=band_get_band(b);
band->alexTxAntenna=ant;
- set_alex_tx_antenna();
+ radio_band_changed();
}
}
#include "vfo.h"
int audio = 0;
-static GMutex audio_mutex;
+GMutex audio_mutex;
static snd_pcm_t *record_handle=NULL;
static snd_pcm_format_t record_audio_format;
extern AUDIO_DEVICE input_devices[MAX_AUDIO_DEVICES];
extern int n_output_devices;
extern AUDIO_DEVICE output_devices[MAX_AUDIO_DEVICES];
+extern GMutex audio_mutex;
extern int audio_open_input();
extern void audio_close_input();
snprintf(label,32,"%lld %s",vfo[0].frequency,mode_string[vfo[0].mode]);
gtk_button_set_label(GTK_BUTTON(last_bandstack), label);
}
+
set_button_text_color(last_bandstack,"black");
last_bandstack=widget;
set_button_text_color(last_bandstack,"orange");
static void cw_keyer_sidetone_frequency_value_changed_cb(GtkWidget *widget, gpointer data) {
cw_keyer_sidetone_frequency=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
-/*
- int txmode=get_tx_mode();
- if(txmode==modeCWL || txmode==modeCWU) {
- BANDSTACK_ENTRY *entry=bandstack_entry_get_current();
- FILTER* band_filters=filters[entry->mode];
- FILTER* band_filter=&band_filters[entry->filter];
- //setFilter(band_filter->low,band_filter->high);
- set_filter(active_receiver,band_filter->low,band_filter->high);
- }
-*/
cw_changed();
receiver_filter_changed(active_receiver);
// changing the side tone frequency affects BFO frequency offsets
static void agc_hang_threshold_value_changed_cb(GtkWidget *widget, gpointer data) {
active_receiver->agc_hang_threshold=(int)gtk_range_get_value(GTK_RANGE(widget));
- set_agc(active_receiver);
+ set_agc(active_receiver, active_receiver->agc);
}
static void pre_post_agc_cb(GtkToggleButton *widget, gpointer data) {
#include "store.h"
-//
-// Re-structuring of the rigctl, MIDI, and gpio code
-// eliminates the need for many "ext" functions
-// defined here.
-//
-
-//
-// Some "helper" functions defined in this file
-// are moved to the top of the file, since they
-// eventually are moved elsewhere.
-//
-
-//
-// 'Helper' functions:
-// -------------------
-//
-// - set_frequency(int id, long long f) // Set the frequency of VFO #v
-// - band_plus(int id) // Move VFO #id to next higher band
-// - band_minus(int id) // Move VFO #id to next lower band
-// - ctun_update(int id, int state) // set CTUN state of VFO #id
-// - set_split(int state) // Set split mode to state
-// - num_pad(int val) // enter VFO frequency
-//
-
-void set_frequency(int v,long long f) {
- int b=get_band_from_frequency(f);
- if(active_receiver->id==v) {
- if (b != vfo[v].band) {
- vfo_band_changed(active_receiver->id,b);
- }
- setFrequency(f);
- } else if(v==VFO_B) {
- // just changing VFO-B frequency
- vfo[v].frequency=f;
- vfo[v].band=b;
- if(receivers==2) {
- // need to change the receiver frequency
- }
- }
-}
-
-void band_plus(int id) {
- long long frequency_min=radio->frequency_min;
- long long frequency_max=radio->frequency_max;
- int b=vfo[id].band;
- BAND *band;
- int found=0;
- while(!found) {
- b++;
- if(b>=BANDS+XVTRS) b=0;
- band=(BAND*)band_get_band(b);
- if(strlen(band->title)>0) {
- if(b<BANDS) {
- if(!(band->frequencyMin==0.0 && band->frequencyMax==0.0)) {
- if(band->frequencyMin<frequency_min || band->frequencyMax>frequency_max) {
- continue;
- }
- }
- }
- vfo_band_changed(id,b);
- found=1;
- }
- }
-}
+// The following calls functions can be called usig g_idle_add
-void band_minus(int id) {
- long long frequency_min=radio->frequency_min;
- long long frequency_max=radio->frequency_max;
- int b=vfo[id].band;
- BAND *band;
- int found=0;
- while(!found) {
- b--;
- if(b<0) b=BANDS+XVTRS-1;
- band=(BAND*)band_get_band(b);
- if(strlen(band->title)>0) {
- if(b<BANDS) {
- if(band->frequencyMin<frequency_min || band->frequencyMax>frequency_max) {
- continue;
- }
- }
- vfo_band_changed(id,b);
- found=1;
- }
- }
-}
-
-void ctun_update(int id,int state) {
- vfo[id].ctun=state;
- if(!vfo[id].ctun) {
- vfo[id].offset=0;
- }
- vfo[id].ctun_frequency=vfo[id].frequency;
- set_offset(receiver[id],vfo[id].offset);
+int ext_menu_filter(void *data) {
+ start_filter();
+ return 0;
}
-void set_split(int val) {
- if (can_transmit) {
- split=val;
- tx_set_mode(transmitter,get_tx_mode());
- set_alex_tx_antenna();
- calcDriveLevel();
- g_idle_add(ext_vfo_update, NULL);
- }
+int ext_menu_mode(void *data) {
+ start_mode();
+ return 0;
}
-void num_pad(int val) {
+int ext_num_pad(void *data) {
+ gint val=GPOINTER_TO_INT(data);
RECEIVER *rx=active_receiver;
if(!vfo[rx->id].entering_frequency) {
vfo[rx->id].entered_frequency=0;
case -2: // enter
if(vfo[rx->id].entered_frequency!=0) {
vfo[rx->id].frequency=vfo[rx->id].entered_frequency;
- if(vfo[rx->id].ctun) {
+ if(vfo[rx->id].ctun) {
vfo[rx->id].ctun=FALSE;
vfo[rx->id].offset=0;
vfo[rx->id].ctun_frequency=vfo[rx->id].frequency;
- }
+ }
}
vfo[rx->id].entering_frequency=FALSE;
break;
break;
}
vfo_update();
-}
-
-//
-// Functions to be invoked through the GTK idle queue,
-//
-
-int ext_menu_filter(void *data) {
- start_filter();
- return 0;
-}
-
-int ext_menu_mode(void *data) {
- start_mode();
- return 0;
-}
-
-int ext_num_pad(void *data) {
- gint val=GPOINTER_TO_INT(data);
- num_pad(val);
return 0;
}
// behave as if the user had chosen the new band
// via the menu prior to changing the frequency
//
- SET_FREQUENCY *SetFreq=(SET_FREQUENCY *)data;
-g_print("ext_set_frequency: vfo=%d freq=%lld\n",SetFreq->vfo,SetFreq->frequency);
- set_frequency(SetFreq->vfo,SetFreq->frequency);
+ SET_FREQUENCY *set_frequency=(SET_FREQUENCY *)data;
+g_print("ext_set_frequency: vfo=%d freq=%lld\n",set_frequency->vfo,set_frequency->frequency);
+ vfo_set_frequency(set_frequency->vfo,set_frequency->frequency);
free(data);
return 0;
}
return 0;
}
+void band_plus(int id) {
+ long long frequency_min=radio->frequency_min;
+ long long frequency_max=radio->frequency_max;
+ int b=vfo[id].band;
+ BAND *band;
+ int found=0;
+ while(!found) {
+ b++;
+ if(b>=BANDS+XVTRS) b=0;
+ band=(BAND*)band_get_band(b);
+ if(strlen(band->title)>0) {
+ if(b<BANDS) {
+ if(!(band->frequencyMin==0.0 && band->frequencyMax==0.0)) {
+ if(band->frequencyMin<frequency_min || band->frequencyMax>frequency_max) {
+ continue;
+ }
+ }
+ }
+ vfo_band_changed(id,b);
+ found=1;
+ }
+ }
+}
+
int ext_band_select(void *data) {
int b=GPOINTER_TO_INT(data);
g_print("%s: %d\n",__FUNCTION__,b);
return 0;
}
+
+void band_minus(int id) {
+ long long frequency_min=radio->frequency_min;
+ long long frequency_max=radio->frequency_max;
+ int b=vfo[id].band;
+ BAND *band;
+ int found=0;
+ while(!found) {
+ b--;
+ if(b<0) b=BANDS+XVTRS-1;
+ band=(BAND*)band_get_band(b);
+ if(strlen(band->title)>0) {
+ if(b<BANDS) {
+ if(band->frequencyMin<frequency_min || band->frequencyMax>frequency_max) {
+ continue;
+ }
+ }
+ vfo_band_changed(id,b);
+ found=1;
+ }
+ }
+}
+
int ext_band_minus(void *data) {
band_minus(active_receiver->id);
return 0;
return 0;
}
+void ctun_update(int id,int state) {
+ vfo[id].ctun=state;
+ if(!vfo[id].ctun) {
+ vfo[id].offset=0;
+ }
+ vfo[id].ctun_frequency=vfo[id].frequency;
+ set_offset(receiver[id],vfo[id].offset);
+}
+
int ext_ctun_update(void *data) {
ctun_update(active_receiver->id,vfo[active_receiver->id].ctun==1?0:1);
g_idle_add(ext_vfo_update, NULL);
if(active_receiver->agc>+AGC_LAST) {
active_receiver->agc=0;
}
- set_agc(active_receiver);
+ set_agc(active_receiver, active_receiver->agc);
g_idle_add(ext_vfo_update, NULL);
return 0;
}
int ext_split_toggle(void *data) {
if(can_transmit) {
- set_split(!split);
+ radio_set_split(!split);
g_idle_add(ext_vfo_update, NULL);
}
return 0;
temp=active_receiver->pan;
int vfo=freq_command->id;
long long f=ntohll(freq_command->hz);
- set_frequency(vfo,f);
+ vfo_set_frequency(vfo,f);
vfo_update();
send_vfo_data(client,VFO_A);
send_vfo_data(client,VFO_B);
extern int ext_snb_update(void *data);
extern int ext_anf_update(void *data);
extern int ext_band_select(void *data);
-
+extern void band_plus(int id);
extern int ext_band_plus(void *data);
+extern void band_minus(int id);
extern int ext_band_minus(void *data);
extern int ext_bandstack_plus(void *data);
extern int ext_bandstack_minus(void *data);
extern int ext_mode_minus(void *data);
extern int ext_b_to_a(void *data);
extern int ext_a_swap_b(void *data);
+extern void ctun_update(int id,int state);
extern int ext_ctun_update(void *data);
extern int ext_agc_update(void *data);
extern int ext_split_toggle(void *data);
-extern int ext_set_split(void *data);
extern int ext_cw_setup();
extern int ext_remote_set_pan(void *data);
extern int ext_set_title(void *data);
-extern int ext_store_memory_slot(void *data);
-extern int ext_recall_memory_slot(void *data);
-//
-// Helper functions, will be moved elsewhere on the long run
-//
-extern void set_split(int val);
-extern void set_frequency(int v,long long f);
-extern void ctun_update(int id,int state);
-extern void band_plus(int id);
-extern void band_minus(int id);
-extern void num_pad(int num);
static gboolean deviation_select_cb (GtkWidget *widget, gpointer data) {
active_receiver->deviation=GPOINTER_TO_UINT(data);
transmitter->deviation=GPOINTER_TO_UINT(data);
- if(active_receiver->deviation==2500) {
- set_filter(active_receiver,-5500,5500);
- tx_set_filter(transmitter);
- } else {
- set_filter(active_receiver,-8000,8000);
- tx_set_filter(transmitter);
- }
+ set_filter(active_receiver);
+ tx_set_filter(transmitter);
set_deviation(active_receiver);
transmitter_set_deviation(transmitter);
set_button_text_color(last_filter,"black");
filter->low=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
if(vfo[id].mode==modeCWL || vfo[id].mode==modeCWU) {
- // Note that a 250 Hz filter has high=low=125 in CW
- filter->high=filter->low=filter->low/2;
+ // Note a 250-Hz-wide CW filter has high=low=125
+ filter->low=filter->low/2;
+ filter->high=filter->low;
}
if(f==vfo[id].filter) {
vfo_filter_changed(f);
}
static gpointer rotary_encoder_thread(gpointer data) {
- PROCESS_ACTION *a;
int i;
+ enum ACTION action;
+ enum ACTION_MODE mode;
+ gint val;
usleep(250000);
g_print("%s\n",__FUNCTION__);
for(i=0;i<MAX_ENCODERS;i++) {
if(encoders[i].bottom_encoder_enabled && encoders[i].bottom_encoder_pos!=0) {
//g_print("%s: BOTTOM encoder %d pos=%d\n",__FUNCTION__,i,encoders[i].bottom_encoder_pos);
- a=g_new(PROCESS_ACTION,1);
- a->action=encoders[i].bottom_encoder_function;
- a->mode=RELATIVE;
- if(a->action==VFO && vfo_encoder_divisor>1) {
- a->val=encoders[i].bottom_encoder_pos/vfo_encoder_divisor;
- encoders[i].bottom_encoder_pos=encoders[i].bottom_encoder_pos-(a->val*vfo_encoder_divisor);
+ action=encoders[i].bottom_encoder_function;
+ mode=RELATIVE;
+ if(action==VFO && vfo_encoder_divisor>1) {
+ val=encoders[i].bottom_encoder_pos/vfo_encoder_divisor;
+ encoders[i].bottom_encoder_pos=encoders[i].bottom_encoder_pos-(val*vfo_encoder_divisor);
} else {
- a->val=encoders[i].bottom_encoder_pos;
+ val=encoders[i].bottom_encoder_pos;
encoders[i].bottom_encoder_pos=0;
}
- if(a->val!=0) g_idle_add(process_action,a); else g_free(a);
+ if(val!=0) schedule_action(action, mode, val);
}
if(encoders[i].top_encoder_enabled && encoders[i].top_encoder_pos!=0) {
//g_print("%s: TOP encoder %d pos=%d\n",__FUNCTION__,i,encoders[i].top_encoder_pos);
- a=g_new(PROCESS_ACTION,1);
- a->action=encoders[i].top_encoder_function;
- a->mode=RELATIVE;
- if(a->action==VFO && vfo_encoder_divisor>1) {
- a->val=encoders[i].top_encoder_pos/vfo_encoder_divisor;
- encoders[i].top_encoder_pos=encoders[i].top_encoder_pos-(a->val*vfo_encoder_divisor);
+ action=encoders[i].top_encoder_function;
+ mode=RELATIVE;
+ if(action==VFO && vfo_encoder_divisor>1) {
+ val=encoders[i].top_encoder_pos/vfo_encoder_divisor;
+ encoders[i].top_encoder_pos=encoders[i].top_encoder_pos-(val*vfo_encoder_divisor);
} else {
- a->val=encoders[i].top_encoder_pos;
+ val=encoders[i].top_encoder_pos;
encoders[i].top_encoder_pos=0;
}
- if(a->val!=0) g_idle_add(process_action,a); else g_free(a);
+ if(val!=0) schedule_action(action, mode, val);
}
}
g_mutex_unlock(&encoder_mutex);
g_mutex_unlock(&encoder_mutex);
}
-void do_switch_action(enum ACTION action, enum ACTION_MODE mode) {
-
- //
- // for a given action with mode==PRESSED or mode==RELEASED,
- // put the "action" into the GTK idle queue. This is done here because
- // we want to process CW events outside the GTK queue
- // (this is also called from i2c.c)
-
- PROCESS_ACTION *a;
-
- switch (action) {
- case CW_KEYER:
- if (mode == PRESSED && cw_keyer_internal == 0) {
- cw_key_down=960000; // max. 20 sec to protect hardware
- cw_key_up=0;
- cw_key_hit=1;
- } else {
- cw_key_down=0;
- cw_key_up=0;
- }
- break;
-#ifdef LOCALCW
- case CW_LEFT:
- keyer_event(1, CW_ACTIVE_LOW ? (mode==PRESSED) : (mode==RELEASED));
- break;
- case CW_RIGHT:
- keyer_event(0, CW_ACTIVE_LOW ? (mode==PRESSED) : (mode==RELEASED));
- break;
-#endif
- default:
- a=g_new(PROCESS_ACTION,1);
- a->action=action;
- a->mode=mode;
- g_idle_add(process_action,a);
- }
-
-
-}
-
-static void process_edge(int offset,int value) {
+static void process_edge(int offset, enum ACTION_MODE value) {
gint i;
unsigned int t;
gboolean found;
return;
}
encoders[i].switch_debounce=t+settle_time;
- do_switch_action(encoders[i].switch_function, value);
+ schedule_action(encoders[i].switch_function, value, 0);
found=TRUE;
break;
}
}
//g_print("%s: switches=%p function=%d (%s)\n",__FUNCTION__,switches,switches[i].switch_function,sw_string[switches[i].switch_function]);
switches[i].switch_debounce=t+settle_time;
- do_switch_action(switches[i].switch_function, value);
+ schedule_action(switches[i].switch_function, value, 0);
break;
}
}
#else
if((rc=gpiod_ctxless_set_value_ext(gpio_device,SIDETONE_GPIO,level,FALSE,consumer,NULL,NULL,0))<0) {
#endif
- g_print("%s: err=%d\n",__FUNCTION__,rc);
+ g_print("%s: err=%d\n",__FUNCTION__,rc);
}
#endif
}
extern void gpio_save_actions();
extern int gpio_init();
extern void gpio_close();
-extern void do_switch_action();
extern unsigned int millis(); // to allow debouncing in i2c.c
#ifdef LOCALCW
#include "toolbar.h"
#include "vfo.h"
#include "ext.h"
-#ifdef LOCALCW
-#include "iambic.h"
-#endif
char *i2c_device="/dev/i2c-1";
unsigned int i2c_address_1=0X20;
if(i2c_sw[i] & flags) {
// The input line associated with switch #i has triggered an interrupt
flags &= ~i2c_sw[i]; // clear *this* bit in flags
- do_switch_action(switches[i].switch_function, (ints & i2c_sw[i]) ? PRESSED : RELEASED);
+ schedule_action(switches[i].switch_function, (ints & i2c_sw[i]) ? PRESSED : RELEASED, 0);
}
}
}
static int dash_held = 0;
static int key_state = 0;
static int dot_length = 0;
-static int dash_length = 0;
static int dot_samples = 0;
static int dash_samples = 0;
static int kcwl = 0;
//
dot_length = 1200 / cw_keyer_speed;
- // will be 3 * dot length at standard weight
- dash_length = (dot_length * 3 * cw_keyer_weight) / 50;
dot_samples = 57600 / cw_keyer_speed;
dash_samples = (3456 * cw_keyer_weight) / cw_keyer_speed;
static int enforce_cw_vox;
void keyer_event(int left, int state) {
+ //g_print("%s: running=%d left=%d state=%d\n",__FUNCTION__,running,left,state);
if (!running) return;
if (state) {
// This is to remember whether the key stroke interrupts a running CAT CW
case SENDDASH:
//
- // wait for dot being complete
+ // wait for dash being complete
//
if (cw_key_down == 0) {
gpio_cw_sidetone_set(0);
key_state = EXITLOOP;
}
- // time stamp in loop_delay is either the last time stamp from the
- // top of the loop, or the time stamp from the last key-down/key-up transition.
- // wait another milli-second before cycling the outer loop
+ // Sleep such that the "state machine" loop is executed once per milli-second
loop_delay.tv_nsec += interval;
while (loop_delay.tv_nsec >= NSEC_PER_SEC) {
loop_delay.tv_nsec -= NSEC_PER_SEC;
//
char *c=getcwd(wisdom_directory, sizeof(wisdom_directory));
strcpy(&wisdom_directory[strlen(wisdom_directory)],"/");
- g_print("Securing wisdom file in directory: %s\n", wisdom_directory);
+ fprintf(stderr,"Securing wisdom file in directory: %s\n", wisdom_directory);
status_text("Checking FFTW Wisdom file ...");
wisdom_running=1;
pthread_create(&wisdom_thread_id, NULL, wisdom_thread, wisdom_directory);
}
}
-//
-// DL1YCF
-// there is a lot of code repetition in the analog and digital meter cases
-// which should be unified.
-//
if(analog_meter) {
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_paint (cr);
switch(meter_type) {
case SMETER:
{
- level=value + (double)rx_gain_calibration + (double)adc[rx->adc].attenuation - adc[rx->adc].gain;
- if (filter_board == CHARLY25) {
- // preamp/dither encodes the preamp level
- if (rx->preamp) level -= 18.0;
- if (rx->dither) level -= 18.0;
- }
- //
- // Assume that alex_attenuation is set correctly if we have an ALEX board
- //
- if (filter_board == ALEX && rx->adc == 0) {
- level += 10*rx->alex_attenuation;
- }
-
+ level=value; // all corrections now in receiver.c
offset=210.0;
int i;
// value is dBm
text_location=10;
offset=5.0;
- level=value + (double)rx_gain_calibration + (double)adc[rx->adc].attenuation - adc[rx->adc].gain;
- if (filter_board == CHARLY25) {
- // preamp/dither encodes the preamp level
- if (rx->preamp) level -= 18.0;
- if (rx->dither) level -= 18.0;
- }
- //
- // Assume that alex_attenuation is set correctly if we have an ALEX board
- //
- if (filter_board == ALEX && rx->adc == 0) {
- level += 10*rx->alex_attenuation;
- }
+ level=value; // all corrections now in receiver.c
if(meter_width>=114) {
int db=1;
struct desc *next; // Next defined action for a controller/key with that note value (NULL for end of list)
};
-extern struct desc *MidiCommandsTable[129];
+extern struct desc *MidiCommandsTable[129]; // slot #128 is for the pitch-bend
extern int midi_debug;
double dnew;
double *dp;
int *ip;
- PROCESS_ACTION *a;
//g_print("%s: action=%d type=%d val=%d\n",__FUNCTION__,action,type,val);
switch(type) {
case MIDI_KEY:
- if(action==CW_LEFT || action==CW_RIGHT) {
-#ifdef LOCALCW
- keyer_event(action==CW_LEFT,val);
-#else
- g_print("MIDI CW key but compiled without LOCALCW\n");
-#endif
- } else if (action == CW_KEYER) {
- //
- // hard "key-up/down" action WITHOUT break-in
- // intended for MIDI keyers which take care of PTT themselves
- //
- if (val != 0 && cw_keyer_internal == 0) {
- cw_key_down=960000; // max. 20 sec to protect hardware
- cw_key_up=0;
- cw_key_hit=1;
- } else {
- cw_key_down=0;
- cw_key_up=0;
- }
- } else {
- a=g_new(PROCESS_ACTION,1);
- a->action=action;
- a->mode=val?PRESSED:RELEASED;
- g_idle_add(process_action,a);
- }
+ schedule_action(action, val?PRESSED:RELEASED, val);
break;
case MIDI_KNOB:
- a=g_new(PROCESS_ACTION,1);
- a->action=action;
- a->mode=ABSOLUTE;
- a->val=val;
- g_idle_add(process_action,a);
+ schedule_action(action, ABSOLUTE, val);
break;
case MIDI_WHEEL:
- a=g_new(PROCESS_ACTION,1);
- a->action=action;
- a->mode=RELATIVE;
- a->val=val;
- g_idle_add(process_action,a);
+ schedule_action(action, RELATIVE, val);
break;
default:
// other types cannot happen for MIDI
int n_output_devices;
AUDIO_DEVICE output_devices[MAX_AUDIO_DEVICES];
-static GMutex audio_mutex;
+GMutex audio_mutex;
int n_input_devices=0;
int n_output_devices=0;
static GThread *mic_read_thread_id=0;
static gboolean running;
-static GMutex audio_mutex;
+GMutex audio_mutex;
static void source_list_cb(pa_context *context,const pa_source_info *s,int eol,void *data) {
int i;
}
//
-// For HPSDR, only receiver[0]->rx_antenna has an effect
-// The antenna is set according to what is stored in the "band" info
-// We have to call this routine in the HPSDR case each time a band is switched.
+// Upon each band change, the TX mode needs be specified again.
+// For HPSDR, one also needs new RX/TX antenna and PA calibration/disable settings.
+// For TX, this must also happen when changing the active receiver or the "split" status.
+// To avoid code duplications, the necessary actions are bundled here.
+// If the attenuation is stored with the band, this should also be adjusted here
//
-void set_alex_rx_antenna() {
- BAND *band;
- switch (protocol) {
- case ORIGINAL_PROTOCOL:
- band=band_get_band(vfo[VFO_A].band);
- receiver[0]->alex_antenna=band->alexRxAntenna;
- break;
- case NEW_PROTOCOL:
- band=band_get_band(vfo[VFO_A].band);
- receiver[0]->alex_antenna=band->alexRxAntenna;
- schedule_high_priority();
- break;
- }
- // This function is NOT called for SOAPY devices
-}
-
-//
-// For HPSDR, determine which band control the TX and
-// set TX antenna accordingly
-// We have to call this routine
-// in the HPSDR case each time the TX band is switched,
-// which is for each band switch, each time "split" is
-// changed, and in case of "split", each time the active
-// RX changes!
-//
-void set_alex_tx_antenna() {
- BAND *band;
- if (!can_transmit) return;
- switch (protocol) {
- case ORIGINAL_PROTOCOL:
- band=band_get_band(vfo[get_tx_vfo()].band);
- transmitter->alex_antenna=band->alexTxAntenna;
- break;
- case NEW_PROTOCOL:
- band=band_get_band(vfo[get_tx_vfo()].band);
- transmitter->alex_antenna=band->alexTxAntenna;
- schedule_high_priority();
- break;
+void radio_band_changed() {
+ BAND *band;
+ if (protocol == ORIGINAL_PROTOCOL || protocol == NEW_PROTOCOL) {
+ //
+ // Obtain band of VFO-A and transmitter, set ALEX RX/TX antennas
+ //
+ band=band_get_band(vfo[VFO_A].band);
+ receiver[0]->alex_antenna=band->alexRxAntenna;
+ receiver[0]->alex_attenuation=band->alexAttenuation;
+ update_att_preamp();
+ if (can_transmit) {
+ band=band_get_band(vfo[get_tx_vfo()].band);
+ transmitter->alex_antenna=band->alexTxAntenna;
}
- // This function is NOT called for SOAPY devices
+ }
+ if (can_transmit) {
+ tx_set_mode(transmitter,get_tx_mode());
+ calcDriveLevel();
+ }
+
+ if (protocol == NEW_PROTOCOL) {
+ schedule_high_priority(); // possibly update RX/TX antennas
+ schedule_general(); // possibly update PA disable
+ }
}
//
// For HPSDR, only receiver[0]->alex_attenuation has an effect
//
void set_alex_attenuation(int v) {
- receiver[0]->alex_attenuation=v;
+ BAND *band;
+ if (protocol == ORIGINAL_PROTOCOL || protocol == NEW_PROTOCOL) {
+ //
+ // Store new attenuation value in band data structure
+ // Note this is the "old" step-attenuator 10/20/30 dB
+ //
+ band=band_get_band(vfo[VFO_A].band);
+ band->alexAttenuation=v;
+ receiver[0]->alex_attenuation=v;
+ }
if(protocol==NEW_PROTOCOL) {
schedule_high_priority();
}
}
+//
+// Interface to set split state
+//
+void radio_set_split(int val) {
+ if (can_transmit) {
+ split=val;
+ radio_band_changed();
+ g_idle_add(ext_vfo_update, NULL);
+ }
+}
+
void radioRestoreState() {
char name[32];
char *value;
extern int RECEIVERS;
extern int MAX_RECEIVERS;
extern int MAX_DDC;
-
-
#ifdef PURESIGNAL
extern int PS_TX_FEEDBACK;
extern int PS_RX_FEEDBACK;
//extern void init_radio();
extern void radio_change_receivers(int r);
extern void radio_change_sample_rate(int rate);
+extern void radio_band_changed();
+extern void radio_set_split(int v);
extern void setMox(int state);
extern int getMox();
extern void setTune(int state);
extern void radio_set_rf_gain(RECEIVER *rx);
extern void set_attenuation(int value);
-extern void set_alex_rx_antenna(void);
-extern void set_alex_tx_antenna(void);
extern void set_alex_attenuation(int v);
extern int isTransmitting();
static void PA_enable_cb(GtkWidget *widget, gpointer data) {
pa_enabled=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ if (protocol == NEW_PROTOCOL) {
+ schedule_general();
+ }
}
static void duplex_cb(GtkWidget *widget, gpointer data) {
case APOLLO:
case CHARLY25:
// This is most likely not necessary here, but can do no harm
- set_alex_rx_antenna();
- set_alex_tx_antenna();
+ radio_band_changed();
break;
case NONE:
break;
// schedule "General" and "HighPrio" packets for P2
//
if(protocol==NEW_PROTOCOL) {
- filter_board_changed();
+ schedule_general();
schedule_high_priority();
}
//
}
g_signal_connect(sample_rate_combo_box,"changed",G_CALLBACK(sample_rate_cb),radio);
gtk_grid_attach(GTK_GRID(grid),sample_rate_combo_box,col,row,1,1);
- row++;
+ row++;
} else {
GtkWidget *sample_rate_label=gtk_label_new(NULL);
gtk_label_set_markup(GTK_LABEL(sample_rate_label), "<b>Sample Rate:</b>");
g_idle_add(ext_vfo_update,NULL);
g_idle_add(zoompan_active_receiver_changed,NULL);
g_idle_add(sliders_active_receiver_changed,NULL);
- // setup the transmitter mode and filter
- if(can_transmit) {
- // TX band has possibly changed
- tx_set_mode(transmitter,get_tx_mode());
- set_alex_tx_antenna();
- calcDriveLevel();
- }
+ radio_band_changed();
}
gboolean receiver_button_release_event(GtkWidget *widget, GdkEventButton *event, gpointer data) {
// new frequency by just clicking in the panadaper. Futher analysis
// showed that there were "moves" with zero offset arriving between
// pressing and releasing the mouse button.
- // (In fact, on my Macintosh I see zillions of moves with zero offset)
// Accepting such a "move" between a "press" and the next "release" event
// sets "has_moved" and results in a "VFO drag" instead of a "VFO set".
//
}
g_mutex_unlock(&rx->display_mutex);
if(active_receiver==rx) {
- rx->meter=GetRXAMeter(rx->id,smeter)+meter_calibration;
+ //
+ // since rx->meter is used in other places as well (e.g. rigctl),
+ // the value obtained from WDSP is best corrected HERE for
+ // possible gain and attenuation
+ //
+ double level=GetRXAMeter(rx->id,smeter)+meter_calibration;
+ level += (double)rx_gain_calibration + (double)adc[rx->adc].attenuation - adc[rx->adc].gain;
+ if (filter_board == CHARLY25) {
+ // preamp/dither encodes the preamp level
+ if (rx->preamp) level -= 18.0;
+ if (rx->dither) level -= 18.0;
+ }
+ //
+ // Assume that alex_attenuation is set correctly if we have an ALEX board
+ //
+ if (filter_board == ALEX && rx->adc == 0) {
+ level += 10*rx->alex_attenuation;
+ }
+ rx->meter=level;
+
meter_update(rx,SMETER,rx->meter,0.0,0.0,0.0,0.0);
}
return TRUE;
SetRXAMode(rx->id, vfo[rx->id].mode);
}
-void set_filter(RECEIVER *rx,int low,int high) {
- if(vfo[rx->id].mode==modeCWL) {
- rx->filter_low=-cw_keyer_sidetone_frequency-low;
- rx->filter_high=-cw_keyer_sidetone_frequency+high;
- } else if(vfo[rx->id].mode==modeCWU) {
- rx->filter_low=cw_keyer_sidetone_frequency-low;
- rx->filter_high=cw_keyer_sidetone_frequency+high;
- } else {
- rx->filter_low=low;
- rx->filter_high=high;
+void set_filter(RECEIVER *rx) {
+ int m=vfo[rx->id].mode;
+ FILTER *mode_filters=filters[m];
+ FILTER *filter=&mode_filters[vfo[rx->id].filter]; // ignored in FMN
+
+ switch (m) {
+ case modeCWL:
+ rx->filter_low=-cw_keyer_sidetone_frequency-filter->low;
+ rx->filter_high=-cw_keyer_sidetone_frequency+filter->high;
+ break;
+ case modeCWU:
+ rx->filter_low=cw_keyer_sidetone_frequency-filter->low;
+ rx->filter_high=cw_keyer_sidetone_frequency+filter->high;
+ break;
+ case modeFMN:
+ //
+ // FM filter settings are ignored, instead, the filter
+ // size is calculated from the deviation
+ //
+ if(rx->deviation==2500) {
+ rx->filter_low=-5500;
+ rx->filter_high=5500;
+ } else {
+ rx->filter_low=-8000;
+ rx->filter_high=8000;
+ }
+ set_deviation(rx);
+ break;
+ default:
+ rx->filter_low=filter->low;
+ rx->filter_high=filter->high;
+ break;
}
RXASetPassband(rx->id,(double)rx->filter_low,(double)rx->filter_high);
SetRXAFMDeviation(rx->id, (double)rx->deviation);
}
-void set_agc(RECEIVER *rx) {
+void set_agc(RECEIVER *rx, int agc) {
- SetRXAAGCMode(rx->id, rx->agc);
+ SetRXAAGCMode(rx->id, agc);
//SetRXAAGCThresh(rx->id, agc_thresh_point, 4096.0, rx->sample_rate);
SetRXAAGCSlope(rx->id,rx->agc_slope);
SetRXAAGCTop(rx->id,rx->agc_gain);
- switch(rx->agc) {
+ switch(agc) {
case AGC_OFF:
break;
case AGC_LONG:
//
GetRXAAGCHangLevel(rx->id, &rx->agc_hang);
GetRXAAGCThresh(rx->id, &rx->agc_thresh, 4096.0, (double)rx->sample_rate);
+
}
void set_offset(RECEIVER *rx,long long offset) {
BAND *b=band_get_band(vfo[rx->id].band);
rx->alex_antenna=b->alexRxAntenna;
- rx->alex_attenuation=0;
+ rx->alex_attenuation=b->alexAttenuation;
rx->agc=AGC_MEDIUM;
rx->agc_gain=80.0;
0.010, 0.025, 0.0, 0.010, 0);
//
-// It has been reported that the piHPSDR noise blankers do not function
+// It has been reported that the piHPSDR noise blankers do not function
// satisfactorily. I could reproduce this after building an "impulse noise source"
// into the HPSDR simulator, and also confirmed that a popular Windows SDR program
// has much better NB/NB2 performance.
//
create_anbEXT(rx->id,1, rx->buffer_size,rx->sample_rate,0.00001,0.00001,0.00001,0.05, 4.95);
create_nobEXT(rx->id,1,0,rx->buffer_size,rx->sample_rate,0.00001,0.00001,0.00001,0.05, 4.95);
-
+
+ //OLD create_anbEXT(rx->id,1,rx->buffer_size,rx->sample_rate,0.0001,0.0001,0.0001,0.05,20);
+ //OLD create_nobEXT(rx->id,1,0,rx->buffer_size,rx->sample_rate,0.0001,0.0001,0.0001,0.05,20);
+
RXASetNC(rx->id, rx->fft_size);
RXASetMP(rx->id, rx->low_latency);
rx->local_audio=0;
}
}
+
// defer set_agc until here, otherwise the AGC threshold is not computed correctly
- set_agc(rx);
+ set_agc(rx, rx->agc);
return rx;
}
// to the radio's sample rate and therefore may vary.
// Since there is no downstream WDSP receiver her, the only thing
// we have to do here is to adapt the spectrum display of the
- // feedback and must then return (rx->id is not a WDSP channel!)
- //
+ // feedback and *must* then return (rx->id is not a WDSP channel!)
+ //
if (rx->id == PS_RX_FEEDBACK && protocol == ORIGINAL_PROTOCOL) {
rx->pixels = 2* scale * rx->width;
g_free(rx->pixel_samples);
}
void receiver_filter_changed(RECEIVER *rx) {
- int filter_low, filter_high;
- int m=vfo[rx->id].mode;
- if(m==modeFMN) {
- if(rx->deviation==2500) {
- filter_low=-5500;
- filter_high=5500;
- } else {
- filter_low=-8000;
- filter_high=8000;
- }
- set_filter(rx,filter_low,filter_high);
- set_deviation(rx);
- } else {
- FILTER *mode_filters=filters[m];
- FILTER *filter=&mode_filters[vfo[rx->id].filter];
- filter_low=filter->low;
- filter_high=filter->high;
- set_filter(rx,filter_low,filter_high);
- }
-
+ set_filter(rx);
if(can_transmit && transmitter!=NULL) {
- if(transmitter->use_rx_filter) {
+ if(transmitter->use_rx_filter && rx==active_receiver) {
tx_set_filter(transmitter);
- }
+ }
}
}
extern void receiver_change_pan(RECEIVER *rx,double pan);
extern void set_mode(RECEIVER* rx,int m);
-extern void set_filter(RECEIVER *rx,int low,int high);
-extern void set_agc(RECEIVER *rx);
+extern void set_filter(RECEIVER *rx);
+extern void set_agc(RECEIVER *rx, int agc);
extern void set_offset(RECEIVER *rx, long long offset);
extern void set_deviation(RECEIVER *rx);
#include "rigctl_menu.h"
#include "noise_menu.h"
#include "new_protocol.h"
+#ifdef LOCALCW
#include "iambic.h" // declare keyer_update()
+#endif
#include <math.h>
#define NEW_PARSER
typedef struct {GMutex m; } GT_MUTEX;
-GT_MUTEX * mutex_a; // implements atomic updates of cat_control
-GT_MUTEX * mutex_busy; // un-necessary lock in serial thread
+GT_MUTEX * mutex_a;
+GT_MUTEX * mutex_busy;
FILE * out;
int output;
typedef struct _client {
int fd;
+ int fifo; // only needed for serial clients to
+ // indicate this is a FIFO and not a
+ // true serial line
+ int busy; // only needed for serial clients over FIFOs
+ int done; // only needed for serial clients over FIFOs
socklen_t address_length;
struct sockaddr_in address;
GThread *thread_id;
// Looks up entry INDEX_NUM in the command structure and
// returns the command string
//
+
void send_resp (int fd,char * msg) {
- if(rigctl_debug) g_print("RIGCTL: fd=%d RESP=%s\n",fd, msg);
+ if(rigctl_debug) g_print("RIGCTL: RESP=%s\n",msg);
int length=strlen(msg);
int rc;
int count=0;
-
+
//
// Possibly, the channel is already closed. In this case
// give up (rc < 0) or at most try a few times (rc == 0)
// since we are in the GTK idle loop
//
while(length>0) {
- rc=write(fd,msg,length);
+ rc=write(fd,msg,length);
if (rc < 0) return;
if (rc == 0) {
count++;
// Spawn off a thread for handling this new connection
//
client[spare].thread_id = g_thread_new("rigctl client", rigctl_client, (gpointer)&client[spare]);
+ // note that g_thread_new() never returns from a failure.
}
close(server_socket);
command_index++;
if(cmd_input[i]==';') {
command[command_index]='\0';
- if(rigctl_debug) g_print("RIGCTL: fd=%d command=%s\n",client->fd,command);
+ if(rigctl_debug) g_print("RIGCTL: command=%s\n",command);
COMMAND *info=g_new(COMMAND,1);
info->client=client;
info->command=command;
// sets or reads the Step Size
if(command[4]==';') {
// read the step size
- sprintf(reply,"ZZAC%02d;",vfo_get_stepindex());
- send_resp(client->fd,reply) ;
+ sprintf(reply,"ZZAC%02d;",vfo_get_stepindex());
+ send_resp(client->fd,reply) ;
} else if(command[6]==';') {
// set the step size
int i=atoi(&command[4]) ;
send_resp(client->fd,reply) ;
} else if(command[15]==';') {
long long f=atoll(&command[4]);
- set_frequency(VFO_A,f);
+ vfo_set_frequency(VFO_A,f);
vfo_update();
}
break;
send_resp(client->fd,reply) ;
} else if(command[15]==';') {
long long f=atoll(&command[4]);
- set_frequency(VFO_B,f);
+ vfo_set_frequency(VFO_B,f);
vfo_update();
}
break;
send_resp(client->fd,reply) ;
} else if(command[5]==';') {
int val=atoi(&command[4]);
- set_split(val);
+ radio_set_split(val);
}
break;
case 'R': //ZZSR
send_resp(client->fd,reply) ;
} else if(command[5]==';') {
int val=atoi(&command[4]);
- set_split(val);
+ radio_set_split(val);
}
break;
case 'Y': //ZZSY
send_resp(client->fd,reply) ;
} else if(command[13]==';') {
long long f=atoll(&command[2]);
- set_frequency(VFO_A,f);
+ vfo_set_frequency(VFO_A,f);
vfo_update();
}
break;
send_resp(client->fd,reply) ;
} else if(command[13]==';') {
long long f=atoll(&command[2]);
- set_frequency(VFO_B,f);
+ vfo_set_frequency(VFO_B,f);
vfo_update();
}
break;
implemented=FALSE;
break;
}
+ g_idle_add(ext_vfo_update, NULL);
}
break;
case 'S': //FS
send_resp(client->fd,reply) ;
} else if(command[3]==';') {
int val=atoi(&command[2]);
- set_split(val);
+ radio_set_split(val);
}
break;
case 'W': //FW
- // set/read filter width
- // make sure filter is filterVar1
- if(vfo[active_receiver->id].filter!=filterVar1) {
- vfo_filter_changed(filterVar1);
- }
- FILTER *mode_filters=filters[vfo[active_receiver->id].mode];
- FILTER *filter=&mode_filters[filterVar1];
- int val=0;
+ // set/read filter width. Switch to Var1 only when setting
if(command[2]==';') {
+ int val=0;
+ FILTER *mode_filters=filters[vfo[active_receiver->id].mode];
+ FILTER *filter=&mode_filters[vfo[active_receiver->id].filter];
switch(vfo[active_receiver->id].mode) {
case modeCWL:
case modeCWU:
send_resp(client->fd,reply) ;
}
} else if(command[6]==';') {
+ // make sure filter is filterVar1
+ if(vfo[active_receiver->id].filter!=filterVar1) {
+ vfo_filter_changed(filterVar1);
+ }
+ FILTER *mode_filters=filters[vfo[active_receiver->id].mode];
+ FILTER *filter=&mode_filters[filterVar1];
int fw=atoi(&command[2]);
- int val=
filter->low=fw;
switch(vfo[active_receiver->id].mode) {
case modeCWL:
}
break;
case 'H': //SH
- {
- // set/read filter high
- // make sure filter is filterVar1
- if(vfo[active_receiver->id].filter!=filterVar1) {
- vfo_filter_changed(filterVar1);
- }
- FILTER *mode_filters=filters[vfo[active_receiver->id].mode];
- FILTER *filter=&mode_filters[filterVar1];
+ // set/read filter high, switch to Var1 only when setting
if(command[2]==';') {
+ FILTER *mode_filters=filters[vfo[active_receiver->id].mode];
+ FILTER *filter=&mode_filters[vfo[active_receiver->id].filter];
int fh=5;
int high=filter->high;
if(vfo[active_receiver->id].mode==modeLSB) {
sprintf(reply,"SH%02d;",fh);
send_resp(client->fd,reply) ;
} else if(command[4]==';') {
+ // make sure filter is filterVar1
+ if(vfo[active_receiver->id].filter!=filterVar1) {
+ vfo_filter_changed(filterVar1);
+ }
+ FILTER *mode_filters=filters[vfo[active_receiver->id].mode];
+ FILTER *filter=&mode_filters[filterVar1];
int i=atoi(&command[2]);
int fh=100;
switch(vfo[active_receiver->id].mode) {
}
vfo_filter_changed(filterVar1);
}
- }
break;
case 'I': //SI
// enter satellite memory name
implemented=FALSE;
break;
case 'L': //SL
- {
- // set/read filter low
- // make sure filter is filterVar1
- if(vfo[active_receiver->id].filter!=filterVar1) {
- vfo_filter_changed(filterVar1);
- }
- FILTER *mode_filters=filters[vfo[active_receiver->id].mode];
- FILTER *filter=&mode_filters[filterVar1];
+ // set/read filter low, switch to Var1 only when setting
if(command[2]==';') {
+ FILTER *mode_filters=filters[vfo[active_receiver->id].mode];
+ FILTER *filter=&mode_filters[vfo[active_receiver->id].filter];
int fl=2;
int low=filter->low;
if(vfo[active_receiver->id].mode==modeLSB) {
sprintf(reply,"SL%02d;",fl);
send_resp(client->fd,reply) ;
} else if(command[4]==';') {
+ // make sure filter is filterVar1
+ if(vfo[active_receiver->id].filter!=filterVar1) {
+ vfo_filter_changed(filterVar1);
+ }
+ FILTER *mode_filters=filters[vfo[active_receiver->id].mode];
+ FILTER *filter=&mode_filters[filterVar1];
int i=atoi(&command[2]);
int fl=100;
switch(vfo[active_receiver->id].mode) {
}
vfo_filter_changed(filterVar1);
}
- }
break;
case 'M': //SM
// read the S meter
if(rigctl_debug) g_print("RIGCTL: UNIMPLEMENTED COMMAND: %s\n",info->command);
send_resp(client->fd,"?;");
}
+ client->done=1; // possibly inform server that command is finished
g_free(info->command);
g_free(info);
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
- g_print ("RIGCTL: Error %d from tcgetattr", errno);
+ g_print ("RIGCTL: Error %d from tcgetattr\n", errno);
return -1;
}
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
- g_print( "RIGCTL: Error %d from tcsetattr", errno);
+ g_print( "RIGCTL: Error %d from tcsetattr\n", errno);
return -1;
}
return 0;
g_idle_add(ext_vfo_update,NULL);
serial_running=TRUE;
while(serial_running) {
+ //
+ // If the "serial line" is a FIFO, we must not drain it
+ // by reading our own responses (it must go to the other
+ // side). Therefore, wait until 50msec after the last
+ // CAT command of this client has been processed.
+ // If for some reason this does not happen, resume after
+ // waiting for about 500 msec.
+ //
+ while (client->fifo && client->busy > 0) {
+ if (client->done) {
+ // command done, possibly response sent:
+ // wait 50 msec then resume listening
+ usleep(50000L);
+ break;
+ }
+ usleep(50000L);
+ client->busy--;
+ }
+ client->busy=0;
+ client->done=0;
+ // TODO: for a FIFO, read() is blocking so we will "hang" here and the
+ // thread will not terminate when loosing serial_running.
numbytes = read (client->fd, cmd_input, sizeof cmd_input);
if(numbytes>0) {
for(i=0;i<numbytes;i++) {
command_index++;
if(cmd_input[i]==';') {
command[command_index]='\0';
- if(rigctl_debug) g_print("RIGCTL: fd=%d command=%s\n",client->fd,command);
+ if(rigctl_debug) g_print("RIGCTL: command=%s\n",command);
COMMAND *info=g_new(COMMAND,1);
info->client=client;
info->command=command;
g_mutex_lock(&mutex_busy->m);
+ client->busy=10;
g_idle_add(parse_cmd,info);
g_mutex_unlock(&mutex_busy->m);
g_print("serial port fd=%d\n",fd);
- set_interface_attribs (fd, serial_baud_rate, serial_parity);
- set_blocking (fd, 0); // set no blocking
-
CLIENT *serial_client=g_new(CLIENT,1);
serial_client->fd=fd;
+ serial_client->busy=0;
+ serial_client->fifo=0;
+
+ if (set_interface_attribs (fd, serial_baud_rate, serial_parity) == 0) {
+ set_blocking (fd, 0); // set no blocking
+ } else {
+ //
+ // This tells the server that fd is something else
+ // than a serial line
+ //
+ g_print("serial port is probably a FIFO\n");
+ serial_client->fifo=1;
+ }
serial_server_thread_id = g_thread_new( "Serial server", serial_server, serial_client);
return 1;
rigctl_server_thread_id = g_thread_new( "rigctl server", rigctl_server, GINT_TO_POINTER(rigctl_port_base));
if( ! rigctl_server_thread_id )
{
- // NOTREACHED, since program aborts if g_thread_new fails
g_print("g_thread_new failed on rigctl_server\n");
}
}
send_agc_gain(client_socket,active_receiver->id,(int)active_receiver->agc_gain,(int)active_receiver->agc_hang,(int)active_receiver->agc_thresh);
} else {
#endif
- set_agc(active_receiver);
+ set_agc(active_receiver, active_receiver->agc);
#ifdef CLIENT_SERVER
}
#endif
void set_agc_gain(int rx,double value) {
g_print("%s\n",__FUNCTION__);
receiver[rx]->agc_gain=value;
- set_agc(receiver[rx]);
+ set_agc(receiver[rx], receiver[rx]->agc);
if(display_sliders) {
gtk_range_set_value (GTK_RANGE(agc_scale),receiver[rx]->agc_gain);
} else {
setSquelch(rx);
#ifndef COMPRESSION_SLIDER_INSTEAD_OF_SQUELCH
if(display_sliders && rx->id == active_receiver->id) {
- gtk_range_set_value (GTK_RANGE(squelch_scale),active_receiver->squelch);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(squelch_enable),active_receiver->squelch_enable);
+ gtk_range_set_value (GTK_RANGE(squelch_scale),rx->squelch);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(squelch_enable),rx->squelch_enable);
} else {
#endif
if(scale_status!=SQUELCH) {
if(scale_status==NO_ACTION) {
scale_status=SQUELCH;
char title[64];
- sprintf(title,"Squelch RX %d (Hz)",active_receiver->id);
+ sprintf(title,"Squelch RX %d (Hz)",rx->id);
scale_dialog=gtk_dialog_new_with_buttons(title,GTK_WINDOW(top_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(scale_dialog));
squelch_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 100.0, 1.00);
gtk_widget_override_font(squelch_scale, pango_font_description_from_string(SLIDERS_FONT));
- gtk_range_set_value (GTK_RANGE(squelch_scale),active_receiver->squelch);
+ gtk_range_set_value (GTK_RANGE(squelch_scale),rx->squelch);
gtk_widget_set_size_request (squelch_scale, 400, 30);
gtk_widget_show(squelch_scale);
gtk_container_add(GTK_CONTAINER(content),squelch_scale);
gtk_dialog_run(GTK_DIALOG(scale_dialog));
} else {
g_source_remove(scale_timer);
- gtk_range_set_value (GTK_RANGE(squelch_scale),active_receiver->squelch);
+ gtk_range_set_value (GTK_RANGE(squelch_scale),rx->squelch);
scale_timer=g_timeout_add(2000,scale_timeout_cb,NULL);
}
#ifndef COMPRESSION_SLIDER_INSTEAD_OF_SQUELCH
// Step c) will not only change the filter but also store the new setting
// with that mode.
//
- set_frequency(active_receiver->id, new_freq);
+ vfo_set_frequency(active_receiver->id, new_freq);
vfo_mode_changed(mem[index].mode);
vfo_filter_changed(mem[index].filter);
g_idle_add(ext_vfo_update,NULL);
void switch_pressed_cb(GtkWidget *widget, gpointer data) {
gint i=GPOINTER_TO_INT(data);
fprintf(stderr,"%s: %d action=%d\n",__FUNCTION__,i,toolbar_switches[i].switch_function);
- PROCESS_ACTION *a=g_new(PROCESS_ACTION,1);
- a->action=toolbar_switches[i].switch_function;
- a->mode=PRESSED;
- g_idle_add(process_action,a);
+ schedule_action(toolbar_switches[i].switch_function, PRESSED, 0);
}
void switch_released_cb(GtkWidget *widget, gpointer data) {
gint i=GPOINTER_TO_INT(data);
fprintf(stderr,"%s: %d action=%d\n",__FUNCTION__,i,toolbar_switches[i].switch_function);
- PROCESS_ACTION *a=g_new(PROCESS_ACTION,1);
- a->action=toolbar_switches[i].switch_function;
- a->mode=RELEASED;
- g_idle_add(process_action,a);
+ schedule_action(toolbar_switches[i].switch_function, RELEASED, 0);
}
GtkWidget *toolbar_init(int my_width, int my_height, GtkWidget* parent) {
if (tx->use_rx_filter) {
//
// Use only 'compatible' parts of RX filter settings
- // to change TX values (importrant for split operation)
+ // to change TX values (important for split operation)
//
int id=active_receiver->id;
int rxmode=vfo[id].mode;
switch(txmode) {
case modeCWL:
case modeCWU:
- // default filter setting (low=150, high=2850) and "use rx filter" unreasonable here
- // note currently WDSP is by-passed in CW anyway.
+ // Our CW signal is always at zero in IQ space, but note
+ // WDSP is by-passed anyway.
tx->filter_low =-150;
tx->filter_high = 150;
break;
case modeSPEC:
// disregard the "low" value and use (-high, high)
tx->filter_low =-high;
- tx->filter_high=high;
+ tx->filter_high= high;
break;
case modeLSB:
case modeDIGL:
BAND *band=band_get_band(vfo[1].band);
vfo[1].lo=band->frequencyLO+band->errorLO;
}
+ if (protocol == NEW_PROTOCOL) {
+ schedule_general(); // for disablePA
+ }
}
void vfo_apply_mode_settings(int id) {
- int m;
+ int m;
m=vfo[id].mode;
switch(id) {
case 0:
bandstack->current_entry=vfo[id].bandstack;
- // set_alex_attenuation(band->alexAttenuation); // nowhere maintained
receiver_vfo_changed(receiver[0]);
break;
case 1:
}
break;
}
- set_alex_rx_antenna();
-
- if(can_transmit) {
- set_alex_tx_antenna();
- tx_set_mode(transmitter,get_tx_mode());
- //
- // If the band has changed, it is necessary to re-calculate
- // the drive level. Furthermore, possibly the "PA disable"
- // status has changed.
- //
- calcDriveLevel(); // sends HighPrio packet if in new protocol
- }
+ radio_band_changed();
switch(protocol) {
case NEW_PROTOCOL:
switch(id) {
case 0:
bandstack->current_entry=vfo[id].bandstack;
- // set_alex_attenuation(band->alexAttenuation); // nowhere maintained
receiver_vfo_changed(receiver[0]);
break;
case 1:
}
break;
}
-
- set_alex_rx_antenna();
- if(can_transmit) {
- set_alex_tx_antenna();
- tx_set_mode(transmitter,get_tx_mode());
- //
- // I do not think the band can change within this function.
- // But out of paranoia, I consider this possiblity here
- //
- calcDriveLevel(); // sends HighPrio packet if in new protocol
- if (protocol == NEW_PROTOCOL) {
- schedule_general(); // for PA disable
- }
- }
+ //
+ // I do not think the band can change within this function.
+ // But out of paranoia, I consider this possiblity here
+ //
+ radio_band_changed();
g_idle_add(ext_vfo_update,NULL);
}
if(receivers==2) {
receiver_vfo_changed(receiver[1]);
}
- set_alex_rx_antenna();
- if(can_transmit) {
- set_alex_tx_antenna();
- tx_set_mode(transmitter,get_tx_mode());
- calcDriveLevel(); // sends HighPrio packet if in new protocol
- }
+ radio_band_changed();
g_idle_add(ext_vfo_update,NULL);
}
vfo[VFO_A].offset=vfo[VFO_B].offset;
receiver_vfo_changed(receiver[0]);
- set_alex_rx_antenna();
- if(can_transmit) {
- set_alex_tx_antenna();
- tx_set_mode(transmitter,get_tx_mode());
- calcDriveLevel(); // sends HighPrio packet if in new protocol
- }
+ radio_band_changed();
g_idle_add(ext_vfo_update,NULL);
}
if(receivers==2) {
receiver_vfo_changed(receiver[1]);
}
- set_alex_rx_antenna();
- if(can_transmit) {
- set_alex_tx_antenna();
- tx_set_mode(transmitter,get_tx_mode());
- calcDriveLevel(); // sends HighPrio packet if in new protocol
- }
+ radio_band_changed();
g_idle_add(ext_vfo_update,NULL);
}
sid=id==0?1:0;
other_receiver=receiver[sid];
-
+
switch(sat_mode) {
case SAT_NONE:
break;
sid=id==0?1:0;
other_receiver=receiver[sid];
-
+
switch(sat_mode) {
case SAT_NONE:
break;
}
void vfo_update() {
-
+
int id=active_receiver->id;
int txvfo=get_tx_vfo();
// So although I do not feel too well if the actual TX frequency is not
// that on the display, I deactivate the code but leave it here so it
// can quickly be re-activated if one wants.
-//
+//
//
// If RIT or XIT is active, add this to displayed VFO frequency
//
cairo_set_source_rgb(cr, 0.0, 0.65, 0.0);
}
}
- cairo_move_to(cr, 5, 38);
- cairo_set_font_size(cr, DISPLAY_FONT_SIZE4);
+ cairo_move_to(cr, 5, 38);
+ cairo_set_font_size(cr, DISPLAY_FONT_SIZE4);
cairo_show_text(cr, temp_text);
sprintf(temp_text,"VFO B: %0lld.%06lld",bf/(long long)1000000,bf%(long long)1000000);
cairo_set_source_rgb(cr, 0.0, 0.65, 0.0);
}
}
- cairo_move_to(cr, 300, 38);
+ cairo_move_to(cr, 300, 38);
cairo_show_text(cr, temp_text);
#ifdef PURESIGNAL
if(can_transmit) {
- cairo_move_to(cr, 130, 50);
+ cairo_move_to(cr, 120, 50);
if(transmitter->puresignal) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
} else {
cairo_show_text(cr, "PS");
}
#endif
-
+
cairo_move_to(cr, 55, 50);
if(active_receiver->zoom>1) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
// NB and NB2 are mutually exclusive, therefore
// they are put to the same place in order to save
// some space
- cairo_move_to(cr, 155, 50);
+ cairo_move_to(cr, 145, 50);
if(active_receiver->nb) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
cairo_show_text(cr, "NB");
}
// NR and NR2 are mutually exclusive
- cairo_move_to(cr, 180, 50);
+ cairo_move_to(cr, 175, 50);
if(active_receiver->nr) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
cairo_show_text(cr, "NR");
cairo_show_text(cr, "NR");
}
- cairo_move_to(cr, 210, 50);
+ cairo_move_to(cr, 200, 50);
if(active_receiver->anf) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
} else {
}
cairo_show_text(cr, "ANF");
- cairo_move_to(cr, 240, 50);
+ cairo_move_to(cr, 230, 50);
if(active_receiver->snb) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
} else {
}
cairo_show_text(cr, "SNB");
- cairo_move_to(cr, 270, 50);
+ cairo_move_to(cr, 265, 50);
switch(active_receiver->agc) {
case AGC_OFF:
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
// we should display the compressor (level)
//
if(can_transmit) {
- cairo_move_to(cr, 330, 50);
+ cairo_move_to(cr, 335, 50);
if (transmitter->compressor) {
sprintf(temp_text,"CMPR %d",(int) transmitter->compressor_level);
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
cairo_show_text(cr, temp_text);
} else {
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
- cairo_show_text(cr, "CMPR OFF");
+ cairo_show_text(cr, "CMPR");
}
}
//
// Indicate whether an equalizer is active
//
- cairo_move_to(cr, 400, 50);
+ cairo_move_to(cr, 400, 50);
if ((isTransmitting() && enable_tx_equalizer) || (!isTransmitting() && enable_rx_equalizer)) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
} else {
}
cairo_show_text(cr, "EQ");
- cairo_move_to(cr, 500, 50);
+ cairo_move_to(cr, 500, 50);
if(diversity_enabled) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
} else {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
cairo_show_text(cr, temp_text);
- cairo_move_to(cr, 430, 50);
+ cairo_move_to(cr, 425, 50);
if(vfo[id].ctun) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
} else {
}
cairo_show_text(cr, "CTUN");
- cairo_move_to(cr, 470, 50);
+ cairo_move_to(cr, 468, 50);
if(cat_control>0) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
} else {
cairo_show_text(cr, "CAT");
if(can_transmit) {
- cairo_move_to(cr, 500, 15);
+ cairo_move_to(cr, 500, 15);
if(vox_enabled) {
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
} else {
}
cairo_show_text(cr, "Locked");
- cairo_move_to(cr, 260, 18);
+ cairo_move_to(cr, 265, 15);
if(split) {
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
} else {
}
cairo_show_text(cr, "Split");
- cairo_move_to(cr, 260, 28);
+ cairo_move_to(cr, 265, 27);
if(sat_mode!=SAT_NONE) {
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
} else {
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
}
sprintf(temp_text,"DUP");
- cairo_move_to(cr, 260, 38);
+ cairo_move_to(cr, 265, 39);
cairo_set_font_size(cr, DISPLAY_FONT_SIZE2);
cairo_show_text(cr, temp_text);
g_idle_add(ext_vfo_update,NULL);
}
+//
+// Interface to set the frequency, including
+// "long jumps", for which we may have to
+// change the band
+//
+void vfo_set_frequency(int v,long long f) {
+ int b=get_band_from_frequency(f);
+ if(active_receiver->id==v) {
+ if (b != vfo[v].band) {
+ vfo_band_changed(active_receiver->id,b);
+ }
+ setFrequency(f);
+ } else {
+ // change VFO frequency of the non-active receiver
+ vfo[v].frequency=f;
+ vfo[v].band=b;
+ if (vfo[v].ctun) {
+ vfo[v].ctun=FALSE;
+ vfo[v].offset=0;
+ vfo[v].ctun_frequency=vfo[v].frequency);
+ }
+ }
+ radio_band_changed();
+ g_idle_add(vfo_update, NULL);
+}
extern void vfo_rit_update(int rx);
extern void vfo_rit_clear(int rx);
extern void vfo_rit(int rx,int i);
+extern void vfo_set_frequency(int vfo, long long f);
#endif
send_vfo_frequency(client_socket,active_receiver->id,f);
} else {
#endif
- //This is inside a callback so we do not need g_idle_add
- //fp=g_new(SET_FREQUENCY,1);
- //fp->vfo=v;
- //fp->frequency = f;
- //g_idle_add(ext_set_frequency, fp);
- set_frequency(v, f);
+ vfo_set_frequency(v, f);
#ifdef CLIENT_SERVER
}
#endif