From f9f1504307c3b7b4e36bd84c567d17e8271a6db2 Mon Sep 17 00:00:00 2001 From: c vw Date: Thu, 29 Apr 2021 18:35:46 +0200 Subject: [PATCH] SWR alarm and protection (mostly from Davide) --- meter.c | 15 +++++++ meter.h | 2 +- radio.c | 1 + radio.h | 1 + transmitter.c | 115 ++++++++++++++++++++++++++++++------------------ transmitter.h | 2 + tx_menu.c | 32 ++++++++++++++ tx_panadapter.c | 36 ++++++++++++--- 8 files changed, 155 insertions(+), 49 deletions(-) diff --git a/meter.c b/meter.c index ce0a6d7..462a334 100644 --- a/meter.c +++ b/meter.c @@ -445,10 +445,16 @@ if(analog_meter) { cairo_move_to(cr, 80, meter_height-22); cairo_show_text(cr, sf); + if (swr > transmitter->swr_alarm) { + cairo_set_source_rgb(cr, 1.0, 0.2, 0.0); // display SWR in red color + } else { + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); // display SWR in white color + } sprintf(sf,"SWR: %1.1f:1",swr); cairo_move_to(cr, 60, meter_height-12); cairo_show_text(cr, sf); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); sprintf(sf,"ALC: %2.1f dB",alc); cairo_move_to(cr, 60, meter_height-2); cairo_show_text(cr, sf); @@ -680,6 +686,13 @@ if(analog_meter) { cairo_move_to(cr, 10, 35); cairo_show_text(cr, sf); + if (swr > transmitter->swr_alarm) { + cairo_set_source_rgb(cr, 1.0, 0.2, 0.0); // display SWR in red color + } else { + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); // display SWR in white color + } + + cairo_select_font_face(cr, DISPLAY_FONT, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); @@ -688,6 +701,8 @@ if(analog_meter) { cairo_move_to(cr, 10, 55); cairo_show_text(cr, sf); + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); // revert to white color + sprintf(sf,"ALC: %2.1f dB",alc); cairo_move_to(cr, meter_width/2, 35); cairo_show_text(cr, sf); diff --git a/meter.h b/meter.h index 7c3bfd2..212b479 100644 --- a/meter.h +++ b/meter.h @@ -26,6 +26,6 @@ #define POWER 1 extern GtkWidget* meter_init(int width,int height,GtkWidget *parent); -extern void meter_update(RECEIVER *rx,int meter_type,double value,double reverse,double exciter,double alc); +extern void meter_update(RECEIVER *rx,int meter_type,double value,double reverse,double alc,double swr); #endif diff --git a/radio.c b/radio.c index 4e4b511..c86aa7d 100644 --- a/radio.c +++ b/radio.c @@ -339,6 +339,7 @@ gboolean mute_rx_while_transmitting=FALSE; double drive_max=100; gboolean display_sequence_errors=TRUE; +gboolean display_swr_protection=FALSE; gint sequence_errors=0; gint rx_height; diff --git a/radio.h b/radio.h index 9b322cc..82f1491 100644 --- a/radio.h +++ b/radio.h @@ -320,6 +320,7 @@ extern int rx_gain_calibration; // position of the RX gain slider that extern double drive_max; extern gboolean display_sequence_errors; +extern gboolean display_swr_protection; extern gint sequence_errors; extern GMutex property_mutex; diff --git a/transmitter.c b/transmitter.c index d0f49ee..6232926 100644 --- a/transmitter.c +++ b/transmitter.c @@ -236,6 +236,12 @@ void transmitter_save_state(TRANSMITTER *tx) { sprintf(name,"transmitter.%d.tune_use_drive",tx->id); sprintf(value,"%d",tx->tune_use_drive); setProperty(name,value); + sprintf(name,"transmitter.%d.swr_protection",tx->id); + sprintf(value,"%d",tx->swr_protection); + setProperty(name,value); + sprintf(name,"transmitter.%d.swr_alarm",tx->id); + sprintf(value,"%f",tx->swr_alarm); + setProperty(name,value); sprintf(name,"transmitter.%d.drive_level",tx->id); sprintf(value,"%d",tx->drive_level); setProperty(name,value); @@ -337,6 +343,12 @@ void transmitter_restore_state(TRANSMITTER *tx) { sprintf(name,"transmitter.%d.tune_use_drive",tx->id); value=getProperty(name); if(value) tx->tune_use_drive=atoi(value); + sprintf(name,"transmitter.%d.swr_protection",tx->id); + value=getProperty(name); + if(value) tx->swr_protection=atoi(value); + sprintf(name,"transmitter.%d.swr_alarm",tx->id); + value=getProperty(name); + if(value) tx->swr_alarm=atof(value); sprintf(name,"transmitter.%d.drive_level",tx->id); value=getProperty(name); if(value) tx->drive_level=atoi(value); @@ -467,7 +479,7 @@ static gboolean update_display(gpointer data) { tx_panadapter_update(tx); } - transmitter->alc=GetTXAMeter(tx->id, alc); + tx->alc=GetTXAMeter(tx->id, alc); double constant1=3.3; double constant2=0.095; int fwd_cal_offset=6; @@ -528,20 +540,20 @@ static gboolean update_display(gpointer data) { } fwd_power=fwd_power-fwd_cal_offset; v1=((double)fwd_power/4095.0)*constant1; - transmitter->fwd=(v1*v1)/constant2; + tx->fwd=(v1*v1)/constant2; if(device==DEVICE_HERMES_LITE || device==DEVICE_HERMES_LITE2) { - transmitter->exciter=0.0; + tx->exciter=0.0; } else { ex_power=ex_power-fwd_cal_offset; v1=((double)ex_power/4095.0)*constant1; - transmitter->exciter=(v1*v1)/constant2; + tx->exciter=(v1*v1)/constant2; } - transmitter->rev=0.0; + tx->rev=0.0; if(fwd_power!=0) { v1=((double)rev_power/4095.0)*constant1; - transmitter->rev=(v1*v1)/constant2; + tx->rev=(v1*v1)/constant2; } break; case NEW_PROTOCOL: @@ -585,60 +597,77 @@ static gboolean update_display(gpointer data) { } fwd_power=fwd_power-fwd_cal_offset; v1=((double)fwd_power/4095.0)*constant1; - transmitter->fwd=(v1*v1)/constant2; + tx->fwd=(v1*v1)/constant2; ex_power=exciter_power; ex_power=ex_power-fwd_cal_offset; v1=((double)ex_power/4095.0)*constant1; - transmitter->exciter=(v1*v1)/constant2; + tx->exciter=(v1*v1)/constant2; - transmitter->rev=0.0; + tx->rev=0.0; if(alex_forward_power!=0) { rev_power=alex_reverse_power; v1=((double)rev_power/4095.0)*constant1; - transmitter->rev=(v1*v1)/constant2; + tx->rev=(v1*v1)/constant2; } break; #ifdef SOAPYSDR case SOAPYSDR_PROTOCOL: - transmitter->fwd=0.0; - transmitter->exciter=0.0; - transmitter->rev=0.0; + tx->fwd=0.0; + tx->exciter=0.0; + tx->rev=0.0; break; #endif } - double fwd=compute_power(transmitter->fwd); - double rev=compute_power(transmitter->rev); + // + // compute_power applies the interpolation table + // that corrects the power meter if it has been + // calibrated + // + double fwd=compute_power(tx->fwd); + double rev=compute_power(tx->rev); + +//g_print("transmitter: meter_update: fwd:%f->%f rev:%f->%f ex_fwd=%d alex_fwd=%d alex_rev=%d\n",tx->fwd,fwd,tx->rev,rev,exciter_power,alex_forward_power,alex_reverse_power); + + tx->fwd=fwd; + tx->rev=rev; // // Calculate SWR here such that it is available in DUPLEX mode - // transmitter->swr can be used in other parts of the program to + // tx->swr can be used in other parts of the program to // implement SWR protection etc. // - if (fwd > 0.01 && fwd > 1.01*rev) { + if (tx->fwd > 0.01 && tx->fwd > 1.01*tx->rev) { // // SWR means VSWR (voltage based) but we have the forward and // reflected power, so correct for that // - double gamma=sqrt(rev/fwd); - transmitter->swr=0.7*(1+gamma)/(1-gamma) + 0.3*transmitter->swr; + double gamma=sqrt(tx->rev/tx->fwd); + tx->swr=0.7*(1+gamma)/(1-gamma) + 0.3*tx->swr; } else { // // This value may be used for auto SWR protection, so move towards 1.0 // - transmitter->swr = 0.7 + 0.3*transmitter->swr; - } - if (fwd == 0.0) { - fwd = transmitter->exciter; + tx->swr = 0.7 + 0.3*tx->swr; } + if (tx->fwd <= 0.0) tx->fwd = tx->exciter; -//g_print("transmitter: meter_update: fwd:%f->%f rev:%f->%f ex_fwd=%d alex_fwd=%d alex_rev=%d\n",transmitter->fwd,fwd,transmitter->rev,rev,exciter_power,alex_forward_power,alex_reverse_power); +// +// If SWR is above threshold and SWR protection is enabled, +// set the drive slider to zero. Do not do this while tuning +// + if (tx->swr_protection && !getTune() && tx->swr >= tx->swr_alarm) { + double *dp = malloc(sizeof(double)); + *dp = 0.0; + g_idle_add(ext_set_drive, (gpointer) dp); + display_swr_protection = TRUE; + } if(!duplex) { - meter_update(active_receiver,POWER,fwd,rev,transmitter->alc,transmitter->swr); + meter_update(active_receiver,POWER,tx->fwd,tx->rev,tx->alc,tx->swr); } return TRUE; // keep going @@ -828,6 +857,8 @@ fprintf(stderr,"create_transmitter: id=%d buffer_size=%d mic_sample_rate=%d mic_ tx->dialog_x=-1; tx->dialog_y=-1; tx->swr = 1.0; + tx->swr_protection = FALSE; + tx->swr_alarm=3.0; // default value for SWR protection transmitter_restore_state(tx); @@ -1159,11 +1190,11 @@ static void full_tx_buffer(TRANSMITTER *tx) { // samples are sent with full amplitude. // DL1YCF: include factor 0.00392 since DriveLevel == 255 means full amplitude // - if(tune && !transmitter->tune_use_drive) { - double fac=sqrt((double)transmitter->tune_percent * 0.01); - gain=gain*(double)transmitter->drive_level*fac*0.00392; + if(tune && !tx->tune_use_drive) { + double fac=sqrt((double)tx->tune_percent * 0.01); + gain=gain*(double)tx->drive_level*fac*0.00392; } else { - gain=gain*(double)transmitter->drive_level*0.00392; + gain=gain*(double)tx->drive_level*0.00392; } } if (protocol == ORIGINAL_PROTOCOL && radio->device == DEVICE_HERMES_LITE2) { @@ -1180,11 +1211,11 @@ static void full_tx_buffer(TRANSMITTER *tx) { // int power; double f,g; - if(tune && !transmitter->tune_use_drive) { - f=sqrt((double)transmitter->tune_percent * 0.01); - power=(int)((double)transmitter->drive_level*f); + if(tune && !tx->tune_use_drive) { + f=sqrt((double)tx->tune_percent * 0.01); + power=(int)((double)tx->drive_level*f); } else { - power=transmitter->drive_level; + power=tx->drive_level; } g=-15.0; if (power > 0) { @@ -1525,8 +1556,8 @@ void add_ps_iq_samples(TRANSMITTER *tx, double i_sample_tx,double q_sample_tx, d if(rx_feedback->samples>=rx_feedback->buffer_size) { if(isTransmitting()) { - pscc(transmitter->id, rx_feedback->buffer_size, tx_feedback->iq_input_buffer, rx_feedback->iq_input_buffer); - if(transmitter->displaying && transmitter->feedback) { + pscc(tx->id, rx_feedback->buffer_size, tx_feedback->iq_input_buffer, rx_feedback->iq_input_buffer); + if(tx->displaying && tx->feedback) { Spectrum0(1, rx_feedback->id, 0, 0, rx_feedback->iq_input_buffer); } } @@ -1596,24 +1627,24 @@ void tx_set_ps(TRANSMITTER *tx,int state) { } void tx_set_twotone(TRANSMITTER *tx,int state) { - transmitter->twotone=state; + tx->twotone=state; if(state) { // set frequencies and levels switch(tx->mode) { case modeCWL: case modeLSB: case modeDIGL: - SetTXAPostGenTTFreq(transmitter->id, -900.0, -1700.0); + SetTXAPostGenTTFreq(tx->id, -900.0, -1700.0); break; default: - SetTXAPostGenTTFreq(transmitter->id, 900.0, 1700.0); + SetTXAPostGenTTFreq(tx->id, 900.0, 1700.0); break; } - SetTXAPostGenTTMag (transmitter->id, 0.49, 0.49); - SetTXAPostGenMode(transmitter->id, 1); - SetTXAPostGenRun(transmitter->id, 1); + SetTXAPostGenTTMag (tx->id, 0.49, 0.49); + SetTXAPostGenMode(tx->id, 1); + SetTXAPostGenRun(tx->id, 1); } else { - SetTXAPostGenRun(transmitter->id, 0); + SetTXAPostGenRun(tx->id, 0); } g_idle_add(ext_mox_update,GINT_TO_POINTER(state)); } diff --git a/transmitter.h b/transmitter.h index 7327013..fb5fc90 100644 --- a/transmitter.h +++ b/transmitter.h @@ -103,6 +103,8 @@ typedef struct _transmitter { double rev; double alc; double swr; + gboolean swr_protection; + double swr_alarm; gint xit_enabled; long long xit; diff --git a/tx_menu.c b/tx_menu.c index ef7e478..a7f30a8 100644 --- a/tx_menu.c +++ b/tx_menu.c @@ -126,6 +126,14 @@ static void tune_percent_cb (GtkWidget *widget, gpointer data) { transmitter->tune_percent=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)); } +static void swr_protection_cb (GtkWidget *widget, gpointer data) { + transmitter->swr_protection=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); +} + +static void swr_alarm_cb (GtkWidget *widget, gpointer data) { + transmitter->swr_alarm=(double)gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget)); +} + static void use_rx_filter_cb(GtkWidget *widget, gpointer data) { transmitter->use_rx_filter=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); @@ -539,6 +547,30 @@ void tx_menu(GtkWidget *parent) { gtk_grid_attach(GTK_GRID(grid),tune_percent,col,row,1,1); g_signal_connect(tune_percent,"value-changed",G_CALLBACK(tune_percent_cb),NULL); + row++; + col=0; + + GtkWidget *swr_protection_b=gtk_check_button_new_with_label("SWR Protection"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (swr_protection_b), transmitter->swr_protection); + gtk_widget_show(swr_protection_b); + gtk_grid_attach(GTK_GRID(grid),swr_protection_b,col,row,1,1); + g_signal_connect(swr_protection_b,"toggled",G_CALLBACK(swr_protection_cb),NULL); + + col++; + + GtkWidget *swr_alarm_label=gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(swr_alarm_label), "SWR alarm at:"); +#ifdef GTK316 + gtk_label_set_xalign(GTK_LABEL(swr_alarm_label),0); +#endif + gtk_widget_show(swr_alarm_label); + gtk_grid_attach(GTK_GRID(grid),swr_alarm_label,col,row,1,1); + + col++; + GtkWidget *swr_alarm=gtk_spin_button_new_with_range(1.0,10.0,0.1); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(swr_alarm),(double)transmitter->swr_alarm); + gtk_grid_attach(GTK_GRID(grid),swr_alarm,col,row,1,1); + g_signal_connect(swr_alarm,"value-changed",G_CALLBACK(swr_alarm_cb),NULL); gtk_container_add(GTK_CONTAINER(content),grid); diff --git a/tx_panadapter.c b/tx_panadapter.c index 4ca7870..8d1d9cb 100644 --- a/tx_panadapter.c +++ b/tx_panadapter.c @@ -49,7 +49,8 @@ static gdouble hz_per_pixel; static gdouble filter_left=0.0; static gdouble filter_right=0.0; -static gint tx_fifo_count=0; +static gint tx_fifo_count=0; // timer for FIFO underrun message +static gint swr_protection_count=0; // timer for SWR protection message /* Create a new surface of the appropriate size to store our scribbles */ static gboolean @@ -399,22 +400,26 @@ void tx_panadapter_update(TRANSMITTER *tx) { if(duplex) { char text[64]; - cairo_set_source_rgb(cr,1.0,0.0,0.0); + cairo_set_source_rgb(cr,1.0,0.2,0.0); cairo_set_font_size(cr, DISPLAY_FONT_SIZE3); if(transmitter->fwd<0.0001) { - sprintf(text,"FWD: %0.3f",transmitter->exciter); + sprintf(text,"FWD %0.3f W",transmitter->exciter); } else { - sprintf(text,"FWD: %0.3f",transmitter->fwd); + sprintf(text,"FWD %0.1f W",transmitter->fwd); } cairo_move_to(cr,10,15); cairo_show_text(cr, text); - sprintf(text,"REV: %0.3f",transmitter->rev); + // + // Since colour is already red, no special + // action for "high SWR" warning + // + sprintf(text,"SWR 1:%1.1f",transmitter->swr); cairo_move_to(cr,10,30); cairo_show_text(cr, text); - sprintf(text,"ALC: %0.3f",transmitter->alc); + sprintf(text,"ALC %2.1f dB",transmitter->alc); cairo_move_to(cr,10,45); cairo_show_text(cr, text); @@ -429,6 +434,25 @@ void tx_panadapter_update(TRANSMITTER *tx) { */ } + // + // If the SWR protection has been triggered, display message for three seconds + // + if (tx->dialog==NULL && display_swr_protection) { + char text[64]; + cairo_set_source_rgb(cr,1.0,0.2,0.0); + cairo_set_font_size(cr,DISPLAY_FONT_SIZE3); + cairo_move_to(cr, 260.0, 30.0); + sprintf(text,"! High SWR > %2.1f", tx->swr_alarm); + cairo_show_text(cr, text); + cairo_move_to(cr, 260.0, 50.0); + cairo_show_text(cr, "! Drive set to zero"); + swr_protection_count++; + if (swr_protection_count >= 3*tx->fps) { + display_swr_protection = FALSE; + swr_protection_count=0; + } + } + if(tx->dialog==NULL && protocol==ORIGINAL_PROTOCOL && device==DEVICE_HERMES_LITE2) { char text[64]; cairo_set_source_rgb(cr,1.0,1.0,0.0); -- 2.45.2