error_handler.o
$(PROGRAM): $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS)
- $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(LIBS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS)
+ $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) $(LIBS)
all: prebuild $(PROGRAM) $(HEADERS) $(REMOTE_HEADERS) $(USBOZY_HEADERS) $(LIMESDR_HEADERS) $(FREEDV_HEADERS) $(LOCALCW_HEADERS) $(I2C_HEADERS) $(GPIO_HEADERS) $(PSK_HEADERS) $(PURESIGNAL_HEADERS) $(STEMLAB_HEADERS) $(SOURCES) $(REMOTE_SOURCES) $(USBOZY_SOURCES) $(LIMESDR_SOURCES) $(FREEDV_SOURCES) $(I2C_SOURCES) $(GPIO_SOURCES) $(PSK_SOURCES) $(PURESIGNAL_SOURCES) $(STEMLAB_SOURCES)
static GThread *receive_thread_id;
static void start_receive_thread();
static gpointer receive_thread(gpointer arg);
-// DL1YCF changed buffer to uchar*
static void process_ozy_input_buffer(unsigned char *buffer);
static void process_bandscope_buffer(char *buffer);
void ozy_send_buffer();
static long send_sequence=-1;
static int metis_offset=8;
-// DL1YCF changed buffer to uchar*
static int metis_write(unsigned char ep,unsigned char* buffer,int length);
static void metis_start_stop(int command);
-// DL1YCF changed buffer to uchar*
static void metis_send_buffer(unsigned char* buffer,int length);
static void metis_restart();
static gpointer receive_thread(gpointer arg) {
struct sockaddr_in addr;
- // DL1YCF changed from int to socklen_t
socklen_t length;
unsigned char buffer[2048];
int bytes_read;
// get the sequence number
sequence=((buffer[4]&0xFF)<<24)+((buffer[5]&0xFF)<<16)+((buffer[6]&0xFF)<<8)+(buffer[7]&0xFF);
- // DL1YCF: added check on lost packets
if (sequence != last_seq_num+1) {
fprintf(stderr,"SEQ ERROR: last %ld, recvd %ld\n", last_seq_num, sequence);
}
break;
}
}
- // DL1YCF added return statement to make compiler happy.
return NULL;
}
-// Dl1YCF changed buffer to uchar*
static void process_ozy_input_buffer(unsigned char *buffer) {
int i,j;
int r;
dot=(control_in[0]&0x04)==0x04;
local_ptt=ptt;
+ if (dot || dash) external_cw_key_hit=1;
if(vfo[tx_vfo].mode==modeCWL || vfo[tx_vfo].mode==modeCWU) {
local_ptt=ptt|dot|dash;
}
}
if(mode==modeCWU || mode==modeCWL) {
//
-// 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.
+// The default is doing 'external CW', that is,
+// CW is entirely done on the HPSDR board. In this
+// case, we should not set MOX, the PTT switching on
+// the HPSDR board is done by the board itself.
//
- if(tune || vox) {
+// However, if we are doing local CW or tuning,
+// we must put the SDR into TX mode.
+//
+ if(isTransmitting() && (tune || local_cw_is_active)) {
output_buffer[C0]|=0x01;
}
} else {
}
#endif
-// DL1YCF change buffer to uchar*
static int metis_write(unsigned char ep,unsigned char* buffer,int length) {
int i;
static void metis_restart() {
// reset metis frame
- // DL1YCF change == to = in the next line
metis_offset=8;
// reset current rx
ozy_send_buffer();
ozy_send_buffer();
- // DL1YCF: reset for the next commands
current_rx=0;
command=1;
#else
#endif
}
-// DL1YCF changedbuffer to uchar *
static void metis_send_buffer(unsigned char* buffer,int length) {
if(sendto(data_socket,buffer,length,0,(struct sockaddr*)&data_addr,data_addr_length)!=length) {
perror("sendto socket failed for metis_send_data\n");
-/* Copyrieht (C)
+/* Copyright (C)
* 2015 - John Melton, G0ORX/N6LYT
*
* This program is free software; you can redistribute it and/or
double vox_gain=10.0;
double vox_hang=250.0;
int vox=0;
+int local_cw_is_active=0;
+int external_cw_key_hit=0;
int diversity_enabled=0;
double i_rotate[2]={1.0,1.0};
extern double vox_gain;
extern double vox_hang;
extern int vox;
+extern int local_cw_is_active;
+extern int external_cw_key_hit;
extern int diversity_enabled;
extern double i_rotate[2];
#include "rigctl_menu.h"
#include <math.h>
-extern void vox_trigger(int lead_in);
-extern void vox_untrigger();
-
// IP stuff below
#include <sys/socket.h>
#include <arpa/inet.h> //inet_addr
//
void send_dash() {
int TimeToGo;
+ if (external_cw_key_hit) return;
for(;;) {
TimeToGo=cw_key_up+cw_key_down;
if (TimeToGo == 0) break;
void send_dot() {
int TimeToGo;
+ if (external_cw_key_hit) return;
for(;;) {
TimeToGo=cw_key_up+cw_key_down;
if (TimeToGo == 0) break;
void send_space(int len) {
int TimeToGo;
+ if (external_cw_key_hit) return;
for(;;) {
TimeToGo=cw_key_up+cw_key_down;
if (TimeToGo == 0) break;
//
static gpointer rigctl_cw_thread(gpointer data)
{
- int index;
+ int i;
char c;
char local_buf[30];
while (server_running) {
// wait for cw_buf become filled with data
+ // (periodically look every 100 msec)
+ external_cw_key_hit=0;
if (!cw_busy) {
usleep(100000L);
continue;
}
+ strncpy(local_buf, cw_buf, 30);
+ cw_busy=0; // mark buffer free again
+ // these values may have changed
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')) {
+ local_cw_is_active=1;
+ if (!mox) {
+ // activate PTT
+ g_idle_add(ext_ptt_update ,(gpointer)1);
+ g_idle_add(ext_cw_key ,(gpointer)1);
+ // have to wait until it is really there
+ while (!mox) usleep(50000L);
+ // some extra time to settle down
+ usleep(100000L);
+ }
+ i=0;
+ while(((c=local_buf[i++]) != '\0') && !external_cw_key_hit && mox) {
rigctl_send_cw_char(c);
}
- vox_untrigger();
+ //
+ // Either an external CW key has been hit (one connected to the SDR board),
+ // or MOX has manually been switched. In this case swallow any pending
+ // or incoming KY messages for about 0.75 sec. We need this long time since
+ // hamlib waits 0.5 secs after receiving a "KY1" message before trying to
+ // send the next bunch (do PTT update immediately).
+ //
+ if (external_cw_key_hit || !mox) {
+ local_cw_is_active=0;
+ g_idle_add(ext_cw_key ,(gpointer)0);
+ // If an external CW key has been hit, we continue in TX mode
+ // doing CW manually. Otherwise, switch PTT off.
+ if (!external_cw_key_hit) {
+ g_idle_add(ext_ptt_update ,(gpointer)0);
+ }
+ for (i=0; i< 75; i++) {
+ cw_busy=0; // mark buffer free
+ usleep(10000L);
+ }
+ }
+ // If the next message is pending, continue
+ if (cw_busy) continue;
+ local_cw_is_active=0;
+ // In case of an abort of local CW, this has already been done.
+ // It is not too harmful to do it again. In case of "normal termination"
+ // of sending a CAT CW message, we have to do it here.
+ g_idle_add(ext_cw_key ,(gpointer)0);
+ if (!external_cw_key_hit) {
+ g_idle_add(ext_ptt_update ,(gpointer)0);
+ }
}
// We arrive here if the rigctl server shuts down.
+ // This very rarely happens. But we should shut down the
+ // local CW system gracefully, in case we were in the mid
+ // of a transmission
rigctl_cw_thread_id = NULL;
cw_busy=0;
+ g_idle_add(ext_cw_key ,(gpointer)0);
+ g_idle_add(ext_ptt_update ,(gpointer)0);
return NULL;
}
// 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) {
+ // hamlib since I fixed it. Note further that the space immediately
+ // following "KY" is *not* part of the message.
+ // while cleaning up after hitting external CW key, just skip message
+ if (!cw_busy && len > 3 && !external_cw_key_hit) {
// Copy data to buffer
- strncpy(cw_buf, cmd_input+2, 30);
+ strncpy(cw_buf, cmd_input+3, 30);
// Kenwood protocol allows for at most 28 characters, so
// this is pure paranoia
cw_buf[29]=0;
int cw_key_up = 0;
int cw_key_down = 0;
+// cw_shape_buffer will eventually be integrated into TRANSMITTER
static int *cw_shape_buffer = NULL;
int cw_shape = 0;
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);
+ cw_shape_buffer=malloc(sizeof(int)*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",
static void full_tx_buffer(TRANSMITTER *tx) {
long isample;
long qsample;
- double gain,lgain,sidevol;
+ double gain,fgain,sidevol;
int j;
int error;
- int mode,do_shape;
+ int mode;
int sidetone=0;
switch(protocol) {
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);
}
}
- if (do_shape) gain=gain*0.005;
- for(j=0;j<tx->output_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*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:
- // 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);
- break;
- }
+// Two times essentially the same code: moved the check on "pulse shape" case
+// out of the loops for computational efficiency
+
+ if ((mode == modeCWL || mode == modeCWU) && !tune) {
+ //
+ // "pulse shape case":
+ // shape the I/Q samples with the envelope function stored in cw_shape_buffer
+ // and produce side tone (again with shaped pulses)
+ //
+ fgain=gain*0.005; // will be multiplied with cw_shape
+ sidevol= 1.29 * cw_keyer_sidetone_volume; // will be multiplied with cw_shape
+ for(j=0;j<tx->output_samples;j++) {
+ gain=fgain*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);
+ switch(protocol) {
+ case ORIGINAL_PROTOCOL:
+ // produce a side tone for internal CW on the HPSDR board
+ // since we may use getNextSidetoneSample for local audio, we need
+ // an independent instance thereof here. To be nice to the CW
+ // operator, the audio is shaped the same way as the RF
+ sidetone=sidevol * cw_shape_buffer[j] * getNextInternalSideToneSample();
+ old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone);
+ break;
+ case NEW_PROTOCOL:
+ // ToDo: how to produce side-tone on the HPSDR board?
+ new_protocol_iq_samples(isample,qsample);
+ break;
+ }
+ }
+ } else {
+ //
+ // Original code without pulse shaping and without side tone
+ //
+ for(j=0;j<tx->output_samples;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);
+ switch(protocol) {
+ case ORIGINAL_PROTOCOL:
+ old_protocol_iq_samples_with_sidetone(isample,qsample,0);
+ break;
+ case NEW_PROTOCOL:
+ new_protocol_iq_samples(isample,qsample);
+ break;
+ }
+ }
}
}
-
}
void add_mic_sample(TRANSMITTER *tx,short mic_sample) {
mode=vfo[0].mode;
}
- // This is valid if not CW and not tuning
- mic_sample_double=(double)mic_sample/32768.0;
+// silence TX audio not transmitting, if tuning, or
+// when doing CW
- if (tune) mic_sample_double=0.0;
+ if (tune || !isTransmitting() || mode==modeCWL || mode==modeCWU) {
+ mic_sample_double=0.0;
+ } else {
+ mic_sample_double=(double)mic_sample/32768.0;
+ }
+
+//This statement takes care that the cw shape buffer is
+//automatically wiped if we are not doing local CW
+
+ cw_shape_buffer[tx->samples]=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
+// RigCtl CW 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
//
}
}
cw_audio_write(0.00003937 * getNextSideToneSample() * cw_keyer_sidetone_volume * cw_shape);
+ cw_shape_buffer[tx->samples]=cw_shape;
+ } else {
+//
+// Have to reset pulse shaper since RigCtl may wait forever
+//
+ cw_key_up=0;
+ cw_key_down=0;
+ cw_shape=0;
}
}
- 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++;
#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);
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;
return result;
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;i<tx->buffer_size;i++) {
sample=tx->mic_input_buffer[i*2];
if(sample<0.0) {
}
}
-// 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);
extern void update_vox(TRANSMITTER *tx);
extern void vox_cancel();
+extern void vox_cw_cancel();
extern double vox_get_peak();