From 066f3ff225b86f71a112c2c987f3783846dfe64c Mon Sep 17 00:00:00 2001 From: John Melton g0orx/n6lyt Date: Sat, 11 Jun 2016 06:42:04 +0000 Subject: [PATCH] added FreeDV support --- Makefile | 46 +- audio.c | 146 ++++++ audio.h | 24 + band.c | 17 +- band.h | 7 +- discovered.h | 26 +- filter.c | 24 +- freedv.c | 125 +++++ freedv.h | 18 + frequency.c | 9 +- gpio.c | 5 +- lime_discovery.c | 74 ++- lime_protocol.c | 365 +++++++++++++ lime_protocol.h | 7 + main.c | 182 +++++-- menu.c | 1020 ++++++++++++++++++++----------------- meter.c | 20 + mode.c | 18 +- mode.h | 5 +- new_discovery.c | 46 +- new_protocol.c | 30 +- new_protocol_programmer.c | 4 +- old_discovery.c | 49 +- old_protocol.c | 285 ++++++----- panadapter.c | 47 +- pihpsdr | Bin 513480 -> 535492 bytes radio.c | 84 ++- radio.h | 5 + release/pihpsdr.tar | Bin 3921920 -> 3942400 bytes release/pihpsdr/pihpsdr | Bin 513480 -> 535492 bytes toolbar.c | 7 +- vfo.c | 39 +- wdsp_init.c | 125 +++-- wdsp_init.h | 2 + 34 files changed, 2061 insertions(+), 800 deletions(-) create mode 100644 audio.c create mode 100644 audio.h create mode 100644 freedv.c create mode 100644 freedv.h create mode 100644 lime_protocol.c create mode 100644 lime_protocol.h diff --git a/Makefile b/Makefile index 1ab6bda..763ade8 100644 --- a/Makefile +++ b/Makefile @@ -2,26 +2,40 @@ UNAME_N := $(shell uname -n) CC=gcc LINK=gcc -#required for LimeSDR (uncomment lines below) +#required for LimeSDR (uncomment line below) +#LIMESDR_INCLUDE=LIMESDR + +ifeq ($(LIMESDR_INCLUDE),LIMESDR) #LIMESDR_OPTIONS=-D LIMESDR -#SOAPYSDRLIBS=-lSoapySDR -#LIMESDR_SOURCES= \ -#lime_discovery.c -#LIMESDR_HEADERS= \ -#lime_discovery.h -#LIMESDR_OBJS= \ -#lime_discovery.o +SOAPYSDRLIBS=-lSoapySDR -lpulse-simple -lpulse +LIMESDR_SOURCES= \ +audio.c \ +lime_discovery.c \ +lime_protocol.c +LIMESDR_HEADERS= \ +audio.h \ +lime_discovery.h \ +lime_protocol.h +LIMESDR_OBJS= \ +audio.o \ +lime_discovery.o \ +lime_protocol.o +endif #required for FREEDV (uncomment lines below) -#FREEDV_OPTIONS=-D FREEDV -#FREEDVLIBS=-lcodec2 -#FREEDV_SOURCES= \ -#freedv.c -#FREEDV_HEADERS= \ -#freedv.h -#FREEDV_OBJS= \ -#freedv.o +FREEDV_INCLUDE=FREEDV + +ifeq ($(FREEDV_INCLUDE),FREEDV) +FREEDV_OPTIONS=-D FREEDV +FREEDVLIBS=-lcodec2 +FREEDV_SOURCES= \ +freedv.c +FREEDV_HEADERS= \ +freedv.h +FREEDV_OBJS= \ +freedv.o +endif OPTIONS=-g -D $(UNAME_N) $(LIMESDR_OPTIONS) $(FREEDV_OPTIONS) -O3 GTKINCLUDES=`pkg-config --cflags gtk+-3.0` diff --git a/audio.c b/audio.c new file mode 100644 index 0000000..17e99eb --- /dev/null +++ b/audio.c @@ -0,0 +1,146 @@ +/* Copyright (C) +* 2016 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "audio.h" + +int audio = 0; +int audio_buffer_size = 2016; // samples (both left and right) + +static pa_simple *stream; + +static sem_t audioBufferFull; +static sem_t audioBufferEmpty; +static pthread_t audioThreadId; + +// each buffer contains 63 samples of left and right audio at 16 bits +#define AUDIO_SAMPLES 63 +#define AUDIO_SAMPLE_SIZE 2 +#define AUDIO_CHANNELS 2 +#define AUDIO_BUFFERS 10 +#define AUDIO_BUFFER_SIZE (AUDIO_SAMPLE_SIZE*AUDIO_CHANNELS*audio_buffer_size) + +static unsigned char *audio_buffer; +static int audio_offset=0; + +void* audioThread(void* arg); + +void audio_init() { + + static const pa_sample_spec spec= { + .format = PA_SAMPLE_S16RE, + .rate = 48000, + .channels = 2 + }; + + int error; + +fprintf(stderr,"audio_init audio_buffer_size=%d\n",audio_buffer_size); + + audio_buffer=(unsigned char *)malloc(AUDIO_BUFFER_SIZE); + + if (!(stream = pa_simple_new(NULL, "pihpsdr", PA_STREAM_PLAYBACK, NULL, "playback", &spec, NULL, NULL, &error))) { + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + _exit(1); + } + + int res=sem_init(&audioBufferFull, 0, 0); + if(res!=0) { + fprintf(stderr,"audio_init: sem_init failed for audioBufferFull%d\n", res); + _exit(1); + } + + res=sem_init(&audioBufferEmpty, 0, 0); + if(res!=0) { + fprintf(stderr,"audio_init: sem_init failed for audioBufferEmpty%d\n", res); + _exit(1); + } + + res=pthread_create(&audioThreadId, NULL, audioThread, NULL); + if(res<0) { + fprintf(stderr, "Error creating DFC thread: %d\n", res); + _exit(1); + } + +fprintf(stderr,"... audio_init\n"); +} + + +void audio_write(double* buffer,int samples) { + int i; + int error; + + for(i=0;i>8; + audio_buffer[audio_offset++]=left_sample; + audio_buffer[audio_offset++]=right_sample>>8; + audio_buffer[audio_offset++]=right_sample; + + if(audio_offset==AUDIO_BUFFER_SIZE) { + if (pa_simple_write(stream, audio_buffer, (size_t)AUDIO_BUFFER_SIZE, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); + _exit(1); + } + audio_offset=0; + } + } + +} + +void* audioThread(void* arg) { + int error; + fprintf(stderr,"audioThread running on cpu%d\n", sched_getcpu()); + + while(1) { + +/* + error=sem_post(&audioBufferEmpty); + if(error!=0) { + fprintf(stderr, "audioThread: sem_post failed for audioBufferEmpty: %d\n", error); + _exit(1); + } +*/ + error=sem_wait(&audioBufferFull); + if(error!=0) { + fprintf(stderr, "audioThread: sem_wait failed for audioBufferFull: %d\n", error); + _exit(1); + } + + if (pa_simple_write(stream, audio_buffer, (size_t)AUDIO_BUFFER_SIZE, &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error)); + _exit(1); + } + } +} diff --git a/audio.h b/audio.h new file mode 100644 index 0000000..e6d2da5 --- /dev/null +++ b/audio.h @@ -0,0 +1,24 @@ +/* Copyright (C) +* 2016 - John Melton, G0ORX/N6LYT +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +extern int audio; +extern int audio_buffer_size; + +void audio_init(); +void audio_write(double *buffer,int samples); diff --git a/band.c b/band.c index daf9569..9667ac8 100644 --- a/band.c +++ b/band.c @@ -103,7 +103,10 @@ BANDSTACK_ENTRY bandstack_entries70[] = BANDSTACK_ENTRY bandstack_entries144[] = {{144010000LL,modeCWU,filterF0,200,2800,200,2800}, {144200000LL,modeUSB,filterF5,200,2800,200,2800}, - {144250000LL,modeUSB,filterF5,200,2800,200,2800}}; + {144250000LL,modeUSB,filterF5,200,2800,200,2800}, + {145600000LL,modeFMN,filterF1,200,2800,200,2800}, + {145725000LL,modeFMN,filterF1,200,2800,200,2800}, + {145900000LL,modeFMN,filterF1,200,2800,200,2800}}; BANDSTACK_ENTRY bandstack_entries220[] = {{220010000LL,modeCWU,filterF0,200,2800,200,2800}, @@ -134,6 +137,14 @@ BANDSTACK_ENTRY bandstack_entries3400[] = {{3400010000LL,modeCWU,filterF0,200,2800,200,2800}, {3400100000LL,modeUSB,filterF5,200,2800,200,2800}, {3400300000LL,modeUSB,filterF5,200,2800,200,2800}}; + +BANDSTACK_ENTRY bandstack_entriesAIR[] = + {{118800000LL,modeAM,filterF1,200,2800,200,2800}, + {120000000LL,modeAM,filterF1,200,2800,200,2800}, + {121700000LL,modeAM,filterF1,200,2800,200,2800}, + {124100000LL,modeAM,filterF1,200,2800,200,2800}, + {126600000LL,modeAM,filterF1,200,2800,200,2800}, + {136500000LL,modeAM,filterF1,200,2800,200,2800}}; #endif BANDSTACK_ENTRY bandstack_entriesGEN[] = @@ -161,13 +172,14 @@ BANDSTACK bandstack10={3,1,bandstack_entries10}; BANDSTACK bandstack50={3,1,bandstack_entries50}; #ifdef LIMESDR BANDSTACK bandstack70={3,1,bandstack_entries70}; -BANDSTACK bandstack144={3,1,bandstack_entries144}; +BANDSTACK bandstack144={6,1,bandstack_entries144}; BANDSTACK bandstack220={3,1,bandstack_entries220}; BANDSTACK bandstack430={3,1,bandstack_entries430}; BANDSTACK bandstack902={3,1,bandstack_entries902}; BANDSTACK bandstack1240={3,1,bandstack_entries1240}; BANDSTACK bandstack2300={3,1,bandstack_entries2300}; BANDSTACK bandstack3400={3,1,bandstack_entries3400}; +BANDSTACK bandstackAIR={6,1,bandstack_entriesAIR}; #endif BANDSTACK bandstackGEN={3,1,bandstack_entriesGEN}; BANDSTACK bandstackWWV={5,1,bandstack_entriesWWV}; @@ -193,6 +205,7 @@ BAND bands[BANDS] = {"1240",&bandstack1240,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, {"2300",&bandstack2300,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, {"3400",&bandstack3400,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, + {"AIR",&bandstackAIR,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,30}, #endif {"GEN",&bandstackGEN,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,0}, {"WWV",&bandstackWWV,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB,0}}; diff --git a/band.h b/band.h index 5916e5d..4c35de6 100644 --- a/band.h +++ b/band.h @@ -39,9 +39,10 @@ #define band1240 16 #define band2300 17 #define band3400 18 -#define bandGen 19 -#define bandWWV 20 -#define BANDS 21 +#define bandAIR 19 +#define bandGen 20 +#define bandWWV 21 +#define BANDS 22 #define HAM_BANDS 19 #else #define bandGen 11 diff --git a/discovered.h b/discovered.h index d57010a..a3ac2ef 100644 --- a/discovered.h +++ b/discovered.h @@ -18,6 +18,7 @@ */ #include +#include #define MAX_DEVICES 16 @@ -36,7 +37,7 @@ #define NEW_DEVICE_ORION2 5 #define NEW_DEVICE_HERMES_LITE 6 -#define LIMESDR_DEVICE 0 +#define LIMESDR_USB_DEVICE 0 #define STATE_AVAILABLE 2 #define STATE_SENDING 3 @@ -48,16 +49,23 @@ struct _DISCOVERED { int protocol; int device; - char name[16]; + char name[64]; int software_version; - unsigned char mac_address[6]; int status; - int address_length; - struct sockaddr_in address; - int interface_length; - struct sockaddr_in interface_address; - struct sockaddr_in interface_netmask; - char interface_name[64]; + union { + struct network { + unsigned char mac_address[6]; + int address_length; + struct sockaddr_in address; + int interface_length; + struct sockaddr_in interface_address; + struct sockaddr_in interface_netmask; + char interface_name[64]; + } network; + struct soapy { + SoapySDRKwargs *args; + } soapy; + } info; }; typedef struct _DISCOVERED DISCOVERED; diff --git a/filter.c b/filter.c index 84f12e3..09a57c4 100644 --- a/filter.c +++ b/filter.c @@ -199,6 +199,7 @@ FILTER filterDRM[FILTERS]={ {-3300,3300,"Var2"}*/ }; +#ifdef FREEDV FILTER filterFREEDV[FILTERS]={ {150,5150,"5.0k"}, {150,4550,"4.4k"}, @@ -213,7 +214,24 @@ FILTER filterFREEDV[FILTERS]={ {150,2850,"Var1"}, {150,2850,"Var2"}*/ }; - - -FILTER *filters[]={filterLSB,filterUSB,filterDSB,filterCWL,filterCWU,filterFMN,filterAM,filterDIGU,filterSPEC,filterDIGL,filterSAM,filterDRM,filterFREEDV}; +#endif + + +FILTER *filters[]={ + filterLSB + ,filterUSB + ,filterDSB + ,filterCWL + ,filterCWU + ,filterFMN + ,filterAM + ,filterDIGU + ,filterSPEC + ,filterDIGL + ,filterSAM + ,filterDRM +#ifdef FREEDV + ,filterFREEDV +#endif +}; diff --git a/freedv.c b/freedv.c new file mode 100644 index 0000000..ccdf478 --- /dev/null +++ b/freedv.c @@ -0,0 +1,125 @@ +#include +#include +#include +#include + +#include "radio.h" + +static struct freedv* modem; + +int n_speech_samples; +int n_max_modem_samples; +int n_nom_modem_samples; +short *speech_out; +short *demod_in; +short *speech_in; +short *mod_out; +int rx_samples_in; +int tx_samples_in; +int samples_out; +int nin; + +int freedv_sync; +float freedv_snr; + +int freedv_tx_text_index=0; + +char freedv_rx_text_data[64]; +int freedv_rx_text_data_index=0; + +static void my_put_next_rx_char(void *callback_state, char c) { + fprintf(stderr, "freedv: my_put_next_rx_char: %c sync=%d\n", c, freedv_sync); + if(freedv_sync) { + if(c==0x0D) { + freedv_rx_text_data_index=0; + } else { + freedv_rx_text_data[freedv_rx_text_data_index++]=c; + freedv_rx_text_data[freedv_rx_text_data_index]=0; + if(freedv_rx_text_data_index>=62) { + freedv_rx_text_data_index=0; + } + } + } +} + + +static char my_get_next_tx_char(void *callback_state){ + char c=freedv_tx_text_data[freedv_tx_text_index++]; + if(c==0) { + c=0x0D; + freedv_tx_text_index=0; + } +fprintf(stderr,"freedv: my_get_next_tx_char=%c\n",c); + return c; +} + +void init_freedv() { +fprintf(stderr,"init_freedv\n"); + modem=freedv_open(FREEDV_MODE_1600); + if(modem==NULL) { + fprintf(stderr,"freedv_open: modem is null\n"); + return; + } + + freedv_set_snr_squelch_thresh(modem, -100.0); + freedv_set_squelch_en(modem, 1); + + n_speech_samples = freedv_get_n_speech_samples(modem); + n_max_modem_samples = freedv_get_n_max_modem_samples(modem); + n_nom_modem_samples = freedv_get_n_nom_modem_samples(modem); +fprintf(stderr,"n_speech_samples=%d n_max_modem_samples=%d\n",n_speech_samples, n_max_modem_samples); + speech_out = (short*)malloc(sizeof(short)*n_speech_samples); + demod_in = (short*)malloc(sizeof(short)*n_max_modem_samples); + speech_in = (short*)malloc(sizeof(short)*n_speech_samples); + mod_out = (short*)malloc(sizeof(short)*n_nom_modem_samples); + + nin = freedv_nin(modem); + rx_samples_in=0; + tx_samples_in=0; + freedv_set_callback_txt(modem, &my_put_next_rx_char, &my_get_next_tx_char, NULL); + //freedv_set_callback_protocol(modem, &my_put_next_rx_proto, NULL, NULL); + //freedv_set_callback_data(modem, my_datarx, my_datatx, NULL); + + int rate=freedv_get_modem_sample_rate(modem); +fprintf(stderr,"freedv modem sample rate=%d\n",rate); + + strcpy(freedv_rx_text_data,""); +} + +void close_freedv() { +fprintf(stderr,"freedv_close\n"); + if(modem!=NULL) { + freedv_close(modem); + } else { + fprintf(stderr,"freedv_close: modem is null\n"); + } +} + +int demod_sample_freedv(short sample) { + int nout=0; + demod_in[rx_samples_in]=sample; + rx_samples_in++; + if(rx_samples_in==nin) { + nout=freedv_rx(modem,speech_out,demod_in); + nin=freedv_nin(modem); + rx_samples_in=0; + freedv_get_modem_stats(modem, &freedv_sync, &freedv_snr); + } + return nout; +} + +int mod_sample_freedv(short sample) { + int nout=0; + speech_in[tx_samples_in]=sample; + tx_samples_in++; + if(tx_samples_in==n_speech_samples) { + freedv_tx(modem,mod_out,speech_in); + tx_samples_in=0; + nout=n_nom_modem_samples; + } + return nout; +} + +void freedv_reset_tx_text_index() { + freedv_tx_text_index=0; +} diff --git a/freedv.h b/freedv.h new file mode 100644 index 0000000..bfc2e82 --- /dev/null +++ b/freedv.h @@ -0,0 +1,18 @@ +extern int n_speech_samples; +extern int n_max_modem_samples; +extern short *demod_in; +extern short *speech_out; +extern short *speech_in; +extern short *mod_out; + +extern int freedv_sync; +extern float freedv_snr; + +extern char freedv_rx_text_data[64]; + +void init_freedv(); +void close_freedv(); +int demod_sample_freedv(short sample); +int mod_sample_freedv(short sample); +void reset_freedv_tx_text_index(); + diff --git a/frequency.c b/frequency.c index 9b309d3..ac13728 100644 --- a/frequency.c +++ b/frequency.c @@ -66,7 +66,9 @@ struct frequency_info frequencyInfo[]= {3590000LL, 3590000LL, "80M RTTY DX", band80, TRUE}, {3590001LL, 3599999LL, "80M RTTY", band80, TRUE}, {3600000LL, 3699999LL, "75M Extra SSB", band80, TRUE}, - {3700000LL, 3789999LL, "75M Ext/Adv SSB", band80, TRUE}, + {3700000LL, 3719999LL, "75M Ext/Adv SSB", band80, TRUE}, + {3720000LL, 3723999LL, "75M Digital Voice", band20, TRUE}, + {3724000LL, 3789999LL, "75M Ext/Adv SSB", band80, TRUE}, {3790000LL, 3799999LL, "75M Ext/Adv DX Window", band80, TRUE}, {3800000LL, 3844999LL, "75M SSB", band80, TRUE}, {3845000LL, 3845000LL, "75M SSTV", band80, TRUE}, @@ -122,8 +124,9 @@ struct frequency_info frequencyInfo[]= {14150000LL, 14174999LL, "20M Extra SSB", band20, TRUE}, {14175000LL, 14224999LL, "20M Ext/Adv SSB", band20, TRUE}, {14225000LL, 14229999LL, "20M SSB", band20, TRUE}, - {14230000LL, 14230000LL, "20M SSTV", band20, TRUE}, - {14230000LL, 14284999LL, "20M SSB", band20, TRUE}, + {14230000LL, 14235999LL, "20M SSTV", band20, TRUE}, + {14236000LL, 14239999LL, "20M Digital Voice", band20, TRUE}, + {14240000LL, 14284999LL, "20M SSB", band20, TRUE}, {14285000LL, 14285000LL, "20M SSB QRP Calling Frequency", band20, TRUE}, {14285000LL, 14285999LL, "20M SSB", band20, TRUE}, {14286000LL, 14286000LL, "20M AM Calling Frequency", band20, TRUE}, diff --git a/gpio.c b/gpio.c index b30a50e..c2060a8 100644 --- a/gpio.c +++ b/gpio.c @@ -754,8 +754,9 @@ static int vfo_encoder_changed(void *data) { if(!locked) { int pos=*(int*)data; BANDSTACK_ENTRY* entry=bandstack_entry_get_current(); - entry->frequencyA=entry->frequencyA+(pos*step); - setFrequency(entry->frequencyA); + //entry->frequencyA=entry->frequencyA+(pos*step); + //setFrequency(entry->frequencyA); + setFrequency(entry->frequencyA+ddsOffset+(pos*step)); vfo_update(NULL); } free(data); diff --git a/lime_discovery.c b/lime_discovery.c index 9414208..86979bb 100644 --- a/lime_discovery.c +++ b/lime_discovery.c @@ -1,5 +1,7 @@ #include +#include #include +#include "discovered.h" #include "lime_discovery.h" void lime_discovery() { @@ -7,13 +9,77 @@ void lime_discovery() { size_t length; int i; args.size=0; - SoapySDRKwargs *devices=SoapySDRDevice_enumerate(&args, &length); + SoapySDRKwargs *devs=SoapySDRDevice_enumerate(&args, &length); -fprintf(stderr,"lime_discovery: length=%d devices->size=%d\n",length,devices->size); +fprintf(stderr,"lime_discovery: length=%ld devs->size=%ld\n",length,devs->size); - for(i=0;ikeys[i], devices->vals[i]); + for(i=0;isize;i++) { +fprintf(stderr,"lime_discovery:device key=%s val=%s\n",devs->keys[i], devs->vals[i]); + if(strcmp(devs->keys[i],"name")==0) { + discovered[devices].protocol=LIMESDR_PROTOCOL; + discovered[devices].device=LIMESDR_USB_DEVICE; + strcpy(discovered[devices].name,devs->vals[i]); + discovered[devices].status=STATE_AVAILABLE; + discovered[devices].info.soapy.args=devs; + devices++; + } } +/* + SoapySDRDevice *device=SoapySDRDevice_make(devs); + if(device==NULL) { + fprintf(stderr,"SoapySDRDevice_make failed: %s\n",SoapySDRDevice_lastError()); + return; + } + + SoapySDRKwargs info=SoapySDRDevice_getHardwareInfo(device); + int version=0; + for(i=0;i +#include +#include +#include +#include + +//#define TIMING +#ifdef TIMING +#include +#endif + +#include "audio.h" +#include "channel.h" +#include "discovered.h" +#include "lime_protocol.h" +#include "radio.h" +#include "SoapySDR/Constants.h" +#include "SoapySDR/Device.h" + +static double bandwidth=3000000.0; + +static DISCOVERED *d; +static size_t receiver; +static SoapySDRDevice *lime_device; +static SoapySDRStream *stream; +static int display_width; +static int buffer_size=BUFFER_SIZE; +static int outputsamples; +static int fft_size=4096; +static int dspRate=48000; +static int outputRate=48000; +static float *buffer; +static int max_samples; + +static long long saved_frequency=0LL; +static int saved_antenna=-1; + +static double iqinputbuffer[BUFFER_SIZE*2]; +static double audiooutputbuffer[BUFFER_SIZE*2]; +static int samples=0; + +static pthread_t receive_thread_id; +static void *receive_thread(void* arg); + +static void *resampler; +static int actual_rate; +static double resamples[1024*2]; +static double resampled[1024*2]; + +#ifdef TIMING +static int rate_samples; +#endif + +static int running; + +void lime_protocol_init(int rx,int pixels) { + SoapySDRKwargs args; + int rc; + +fprintf(stderr,"lime_protocol_init: receiver=%d pixels=%d\n",rx,pixels); + + d=&discovered[selected_device]; + receiver=(size_t)rx; + display_width=pixels; + + outputsamples=BUFFER_SIZE/(sample_rate/48000); +/* + switch(sample_rate) { + case 48000: + outputsamples=BUFFER_SIZE; + break; + case 96000: + outputsamples=BUFFER_SIZE/2; + break; + case 192000: + outputsamples=BUFFER_SIZE/4; + break; + case 384000: + outputsamples=BUFFER_SIZE/8; + break; + case 768000: + outputsamples=BUFFER_SIZE/16; + break; + case 1536000: + outputsamples=BUFFER_SIZE/32; + break; + } +*/ + + + + + args.size=0; + + // initialize the radio +fprintf(stderr,"lime_protocol: receive_thread: SoapySDRDevice_make\n"); + lime_device=SoapySDRDevice_make(d->info.soapy.args); + if(lime_device==NULL) { + fprintf(stderr,"lime_protocol: SoapySDRDevice_make failed: %s\n",SoapySDRDevice_lastError()); + _exit(-1); + } + +fprintf(stderr,"lime_protocol: set antenna to NONE\n"); + lime_protocol_set_antenna(0); + +fprintf(stderr,"lime_protocol: setting samplerate=%f\n",(double)sample_rate); + rc=SoapySDRDevice_setSampleRate(lime_device,SOAPY_SDR_RX,receiver,(double)sample_rate); + if(rc!=0) { + fprintf(stderr,"lime_protocol: SoapySDRDevice_setSampleRate(%f) failed: %s\n",(double)sample_rate,SoapySDRDevice_lastError()); + } + + actual_rate=(int)SoapySDRDevice_getSampleRate(lime_device, SOAPY_SDR_RX, receiver); +fprintf(stderr,"lime_protocol: actual samplerate= %d\n",actual_rate); +if(sample_rate==768000 && actual_rate==767999) { + actual_rate=768000; + fprintf(stderr,"lime_protocol: forced actual_rate\n"); +} + +fprintf(stderr,"lime_protocol: setting bandwidth =%f\n",bandwidth); + rc=SoapySDRDevice_setBandwidth(lime_device,SOAPY_SDR_RX,receiver,bandwidth); + if(rc!=0) { + fprintf(stderr,"lime_protocol: SoapySDRDevice_setBandwidth(%f) failed: %s\n",bandwidth,SoapySDRDevice_lastError()); + } + +if(saved_frequency!=0LL) { +fprintf(stderr,"lime_protocol: setting save_frequency: %lld\n",saved_frequency); + lime_protocol_set_frequency(saved_frequency); +} + +/* +fprintf(stderr,"lime_protocol: set baseband frequency\n"); + rc=SoapySDRDevice_setFrequencyComponent(lime_device,SOAPY_SDR_RX,receiver,"BB",0.0,&args); + if(rc!=0) { + fprintf(stderr,"lime_protocol: SoapySDRDevice_setFrequencyComponent(BB) failed: %s\n",SoapySDRDevice_lastError()); + } +*/ + +fprintf(stderr,"setting antennal to LNAL\n"); + lime_protocol_set_antenna(2); + +fprintf(stderr,"setting Gain LNA=30.0\n"); + rc=SoapySDRDevice_setGainElement(lime_device,SOAPY_SDR_RX,receiver,"LNA",30.0); + if(rc!=0) { + fprintf(stderr,"lime_protocol: SoapySDRDevice_setGain LNA failed: %s\n",SoapySDRDevice_lastError()); + } +fprintf(stderr,"setting Gain PGA=19.0\n"); + rc=SoapySDRDevice_setGainElement(lime_device,SOAPY_SDR_RX,receiver,"PGA",19.0); + if(rc!=0) { + fprintf(stderr,"lime_protocol: SoapySDRDevice_setGain PGA failed: %s\n",SoapySDRDevice_lastError()); + } +fprintf(stderr,"setting Gain TIA=12.0\n"); + rc=SoapySDRDevice_setGainElement(lime_device,SOAPY_SDR_RX,receiver,"TIA",12.0); + if(rc!=0) { + fprintf(stderr,"lime_protocol: SoapySDRDevice_setGain TIA failed: %s\n",SoapySDRDevice_lastError()); + } + +fprintf(stderr,"lime_protocol: receive_thread: SoapySDRDevice_setupStream\n"); + size_t channels=(size_t)receiver; + rc=SoapySDRDevice_setupStream(lime_device,&stream,SOAPY_SDR_RX,"CF32",&channels,1,&args); + if(rc!=0) { + fprintf(stderr,"lime_protocol: SoapySDRDevice_setupStream failed: %s\n",SoapySDRDevice_lastError()); + _exit(-1); + } + + max_samples=SoapySDRDevice_getStreamMTU(lime_device,stream); +fprintf(stderr,"max_samples=%d\n",max_samples); + + buffer=(float *)malloc(max_samples*sizeof(float)*2); + + if(actual_rate!=sample_rate) { +fprintf(stderr,"lime_protocol: creating resampler from %d to %d\n",actual_rate,sample_rate); + resampler=create_resample (1, max_samples, resamples, resampled, actual_rate, sample_rate, 0.0, 0, 1.0); + } + + rc=SoapySDRDevice_activateStream(lime_device, stream, 0, 0LL, 0); + if(rc!=0) { + fprintf(stderr,"lime_protocol: SoapySDRDevice_activateStream failed: %s\n",SoapySDRDevice_lastError()); + _exit(-1); + } + + + if(saved_antenna!=-1) { +fprintf(stderr,"lime_protocol: setting save_antenna: %d\n",saved_antenna); + lime_protocol_set_antenna(saved_antenna); + } + + if(saved_frequency!=0LL) { +fprintf(stderr,"lime_protocol: setting save_frequency: %lld\n",saved_frequency); + lime_protocol_set_frequency(saved_frequency); + } + +fprintf(stderr,"lime_protocol_init: audio_init\n"); + audio_init(); + +fprintf(stderr,"lime_protocol_init: create receive_thread\n"); + rc=pthread_create(&receive_thread_id,NULL,receive_thread,NULL); + if(rc != 0) { + fprintf(stderr,"lime_protocol: pthread_create failed on receive_thread: rc=%d\n", rc); + _exit(-1); + } + + + +} + +static void *receive_thread(void *arg) { + float isample; + float qsample; + int outsamples; + int elements; + int flags=0; + long long timeNs=0; + long timeoutUs=10000L; + int i; +#ifdef TIMING + struct timeval tv; + long start_time, end_time; + rate_samples=0; + gettimeofday(&tv, NULL); start_time=tv.tv_usec + 1000000 * tv.tv_sec; +#endif + running=1; +fprintf(stderr,"lime_protocol: receive_thread\n"); + while(running) { + elements=SoapySDRDevice_readStream(lime_device,stream,(void *)&buffer,max_samples,&flags,&timeNs,timeoutUs); +//fprintf(stderr,"read %d elements\n",elements); + if(actual_rate!=sample_rate) { + for(i=0;imac_address[0], - d->mac_address[1], - d->mac_address[2], - d->mac_address[3], - d->mac_address[4], - d->mac_address[5]); + switch(d->protocol) { + case ORIGINAL_PROTOCOL: + case NEW_PROTOCOL: + sprintf(property_path,"%02X-%02X-%02X-%02X-%02X-%02X.props", + d->info.network.mac_address[0], + d->info.network.mac_address[1], + d->info.network.mac_address[2], + d->info.network.mac_address[3], + d->info.network.mac_address[4], + d->info.network.mac_address[5]); + break; +#ifdef LIMESDR + case LIMESDR_PROTOCOL: + sprintf(property_path,"limesdr.props"); + break; +#endif + } radioRestoreState(); GtkWidget *dialog=gtk_dialog_new_with_buttons("Configure",GTK_WINDOW(splash_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL); @@ -582,7 +604,7 @@ static void configure_cb(GtkWidget *widget, gpointer data) { gtk_grid_attach(GTK_GRID(grid),sample_rate_384,0,4,1,1); g_signal_connect(sample_rate_384,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)384000); - if(d->protocol==NEW_PROTOCOL) { + if(d->protocol==NEW_PROTOCOL || d->protocol==LIMESDR_PROTOCOL) { GtkWidget *sample_rate_768=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_384),"768000"); //gtk_widget_override_font(sample_rate_768, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_768), sample_rate==768000); @@ -590,53 +612,78 @@ static void configure_cb(GtkWidget *widget, gpointer data) { gtk_grid_attach(GTK_GRID(grid),sample_rate_768,0,5,1,1); g_signal_connect(sample_rate_768,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)768000); - GtkWidget *sample_rate_1536=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_768),"1536000"); + GtkWidget *sample_rate_921=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_768),"921180"); + //gtk_widget_override_font(sample_rate_921, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_921), sample_rate==921180); + gtk_widget_show(sample_rate_921); + gtk_grid_attach(GTK_GRID(grid),sample_rate_921,0,6,1,1); + g_signal_connect(sample_rate_921,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)921180); + + GtkWidget *sample_rate_1536=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_921),"1536000"); //gtk_widget_override_font(sample_rate_1536, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_1536), sample_rate==1536000); gtk_widget_show(sample_rate_1536); - gtk_grid_attach(GTK_GRID(grid),sample_rate_1536,0,6,1,1); + gtk_grid_attach(GTK_GRID(grid),sample_rate_1536,0,7,1,1); g_signal_connect(sample_rate_1536,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)1536000); + +#ifdef LIMESDR + if(d->protocol==LIMESDR_PROTOCOL) { + GtkWidget *sample_rate_1M=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_1536),"1048576"); + //gtk_widget_override_font(sample_rate_1M, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_1M), sample_rate==1048576); + gtk_widget_show(sample_rate_1M); + gtk_grid_attach(GTK_GRID(grid),sample_rate_1M,0,8,1,1); + g_signal_connect(sample_rate_1M,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)1048576); + + GtkWidget *sample_rate_2M=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(sample_rate_1M),"2097152"); + //gtk_widget_override_font(sample_rate_2M, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sample_rate_2M), sample_rate==2097152); + gtk_widget_show(sample_rate_2M); + gtk_grid_attach(GTK_GRID(grid),sample_rate_2M,0,9,1,1); + g_signal_connect(sample_rate_2M,"pressed",G_CALLBACK(sample_rate_cb),(gpointer *)2097152); + } +#endif } GtkWidget *display_label=gtk_label_new("Display:"); //gtk_widget_override_font(display_label, pango_font_description_from_string("Arial 18")); gtk_widget_show(display_label); - gtk_grid_attach(GTK_GRID(grid),display_label,0,7,1,1); + gtk_grid_attach(GTK_GRID(grid),display_label,0,9,1,1); GtkWidget *b_display_panadapter=gtk_check_button_new_with_label("Display Panadapter"); //gtk_widget_override_font(b_display_panadapter, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_panadapter), display_panadapter); gtk_widget_show(b_display_panadapter); - gtk_grid_attach(GTK_GRID(grid),b_display_panadapter,0,8,1,1); + gtk_grid_attach(GTK_GRID(grid),b_display_panadapter,0,10,1,1); g_signal_connect(b_display_panadapter,"toggled",G_CALLBACK(display_panadapter_cb),(gpointer *)NULL); GtkWidget *b_display_waterfall=gtk_check_button_new_with_label("Display Waterfall"); //gtk_widget_override_font(b_display_waterfall, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_waterfall), display_waterfall); gtk_widget_show(b_display_waterfall); - gtk_grid_attach(GTK_GRID(grid),b_display_waterfall,0,9,1,1); + gtk_grid_attach(GTK_GRID(grid),b_display_waterfall,0,11,1,1); g_signal_connect(b_display_waterfall,"toggled",G_CALLBACK(display_waterfall_cb),(gpointer *)NULL); GtkWidget *b_display_sliders=gtk_check_button_new_with_label("Display Sliders"); //gtk_widget_override_font(b_display_sliders, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_sliders), display_sliders); gtk_widget_show(b_display_sliders); - gtk_grid_attach(GTK_GRID(grid),b_display_sliders,0,10,1,1); + gtk_grid_attach(GTK_GRID(grid),b_display_sliders,0,12,1,1); g_signal_connect(b_display_sliders,"toggled",G_CALLBACK(display_sliders_cb),(gpointer *)NULL); GtkWidget *b_display_toolbar=gtk_check_button_new_with_label("Display Toolbar"); //gtk_widget_override_font(b_display_toolbar, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_display_toolbar), display_toolbar); gtk_widget_show(b_display_toolbar); - gtk_grid_attach(GTK_GRID(grid),b_display_toolbar,0,11,1,1); + gtk_grid_attach(GTK_GRID(grid),b_display_toolbar,0,13,1,1); g_signal_connect(b_display_toolbar,"toggled",G_CALLBACK(display_toolbar_cb),(gpointer *)NULL); GtkWidget *b_toolbar_simulate_buttons=gtk_check_button_new_with_label("Toolbar Simulate Buttons"); //gtk_widget_override_font(b_toolbar_simulate_buttons, pango_font_description_from_string("Arial 18")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_toolbar_simulate_buttons), toolbar_simulate_buttons); gtk_widget_show(b_toolbar_simulate_buttons); - gtk_grid_attach(GTK_GRID(grid),b_toolbar_simulate_buttons,0,12,1,1); + gtk_grid_attach(GTK_GRID(grid),b_toolbar_simulate_buttons,0,14,1,1); g_signal_connect(b_toolbar_simulate_buttons,"toggled",G_CALLBACK(toolbar_simulate_buttons_cb),(gpointer *)NULL); gtk_container_add(GTK_CONTAINER(content),grid); @@ -667,10 +714,18 @@ gboolean main_delete (GtkWidget *widget) { #ifdef INCLUDE_GPIO gpio_close(); #endif - if(protocol==ORIGINAL_PROTOCOL) { - old_protocol_stop(); - } else { - new_protocol_stop(); + switch(protocol) { + case ORIGINAL_PROTOCOL: + old_protocol_stop(); + break; + case NEW_PROTOCOL: + new_protocol_stop(); + break; +#ifdef LIMESDR + case LIMESDR_PROTOCOL: + lime_protocol_stop(); + break; +#endif } radioSaveState(); _exit(0); @@ -782,21 +837,41 @@ gint init(void* arg) { char text[128]; for(i=0;iprotocol,d->name); + switch(d->protocol) { + case ORIGINAL_PROTOCOL: + case NEW_PROTOCOL: + sprintf(text,"%s (%s %d.%d) %s (%02X:%02X:%02X:%02X:%02X:%02X) on %s\n", d->name, d->protocol==ORIGINAL_PROTOCOL?"old":"new", //d->protocol==ORIGINAL_PROTOCOL?d->software_version/10:d->software_version/100, //d->protocol==ORIGINAL_PROTOCOL?d->software_version%10:d->software_version%100, d->software_version/10, d->software_version%10, - inet_ntoa(d->address.sin_addr), - d->mac_address[0], - d->mac_address[1], - d->mac_address[2], - d->mac_address[3], - d->mac_address[4], - d->mac_address[5], - d->interface_name); + inet_ntoa(d->info.network.address.sin_addr), + d->info.network.mac_address[0], + d->info.network.mac_address[1], + d->info.network.mac_address[2], + d->info.network.mac_address[3], + d->info.network.mac_address[4], + d->info.network.mac_address[5], + d->info.network.interface_name); + break; +#ifdef LIMESDR + case LIMESDR_PROTOCOL: +/* + sprintf(text,"%s (%s %d.%d.%d)\n", + d->name, + "lime", + d->software_version/100, + (d->software_version%100)/10, + d->software_version%10); +*/ + sprintf(text,"%s\n", + d->name); + break; +#endif + } GtkWidget *label=gtk_label_new(text); gtk_widget_override_font(label, pango_font_description_from_string("Arial 12")); @@ -816,7 +891,7 @@ gint init(void* arg) { } // if not on the same subnet then cannot start it - if((d->interface_address.sin_addr.s_addr&d->interface_netmask.sin_addr.s_addr) != (d->address.sin_addr.s_addr&d->interface_netmask.sin_addr.s_addr)) { + if((d->info.network.interface_address.sin_addr.s_addr&d->info.network.interface_netmask.sin_addr.s_addr) != (d->info.network.address.sin_addr.s_addr&d->info.network.interface_netmask.sin_addr.s_addr)) { gtk_button_set_label(GTK_BUTTON(start_button),"Subnet!"); gtk_widget_set_sensitive(start_button, FALSE); } @@ -853,13 +928,23 @@ gint init(void* arg) { protocol=d->protocol; device=d->device; - sprintf(property_path,"%02X-%02X-%02X-%02X-%02X-%02X.props", - d->mac_address[0], - d->mac_address[1], - d->mac_address[2], - d->mac_address[3], - d->mac_address[4], - d->mac_address[5]); + switch(d->protocol) { + case ORIGINAL_PROTOCOL: + case NEW_PROTOCOL: + sprintf(property_path,"%02X-%02X-%02X-%02X-%02X-%02X.props", + d->info.network.mac_address[0], + d->info.network.mac_address[1], + d->info.network.mac_address[2], + d->info.network.mac_address[3], + d->info.network.mac_address[4], + d->info.network.mac_address[5]); + break; +#ifdef LIMESDR + case LIMESDR_PROTOCOL: + sprintf(property_path,"limesdr.props"); + break; +#endif + } radioRestoreState(); @@ -868,12 +953,21 @@ gint init(void* arg) { //splash_status("Initializing wdsp ..."); wdsp_init(0,display_width,d->protocol); - if(d->protocol==ORIGINAL_PROTOCOL) { + switch(d->protocol) { + case ORIGINAL_PROTOCOL: splash_status("Initializing old protocol ..."); old_protocol_init(0,display_width); - } else { + break; + case NEW_PROTOCOL: splash_status("Initializing new protocol ..."); new_protocol_init(0,display_width); + break; +#ifdef LIMESDR + case LIMESDR_PROTOCOL: + splash_status("Initializing lime protocol ..."); + lime_protocol_init(0,display_width); + break; +#endif } splash_status("Initializing GPIO ..."); @@ -1012,12 +1106,13 @@ fprintf(stderr,"toolbar_height=%d\n",TOOLBAR_HEIGHT); // save every 30 seconds save_timer_id=gdk_threads_add_timeout(30000, save_cb, NULL); - vfo_update(NULL); - if(protocol==ORIGINAL_PROTOCOL) { + if(protocol!=NEW_PROTOCOL) { setFrequency(getFrequency()); } + g_idle_add(vfo_update,(gpointer)NULL); + return 0; } @@ -1047,6 +1142,7 @@ main (int argc, display_width=gdk_screen_get_width(screen); display_height=gdk_screen_get_height(screen); +fprintf(stderr,"width=%d height=%d\n", display_width, display_height); if(display_width>800 || display_height>480) { display_width=800; display_height=480; diff --git a/menu.c b/menu.c index b43810e..b72b960 100644 --- a/menu.c +++ b/menu.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "band.h" #include "channel.h" @@ -35,6 +36,9 @@ #include "radio.h" #include "version.h" #include "wdsp.h" +#ifdef FREEDV +#include "freedv.h" +#endif static GtkWidget *parent_window; @@ -214,10 +218,18 @@ static gboolean exit_pressed_event_cb (GtkWidget *widget, #ifdef INCLUDE_GPIO gpio_close(); #endif - if(protocol==ORIGINAL_PROTOCOL) { - old_protocol_stop(); - } else { - new_protocol_stop(); + switch(protocol) { + case ORIGINAL_PROTOCOL: + old_protocol_stop(); + break; + case NEW_PROTOCOL: + new_protocol_stop(); + break; +#ifdef LIMESDR + case LIMESDR_PROTOCOL: + lime_protocol_stop(); + break; +#endif } radioSaveState(); _exit(0); @@ -230,10 +242,18 @@ static gboolean reboot_pressed_event_cb (GtkWidget *widget, #ifdef INCLUDE_GPIO gpio_close(); #endif - if(protocol==ORIGINAL_PROTOCOL) { - old_protocol_stop(); - } else { - new_protocol_stop(); + switch(protocol) { + case ORIGINAL_PROTOCOL: + old_protocol_stop(); + break; + case NEW_PROTOCOL: + new_protocol_stop(); + break; +#ifdef LIMESDR + case LIMESDR_PROTOCOL: + lime_protocol_stop(); + break; +#endif } radioSaveState(); system("reboot"); @@ -247,10 +267,18 @@ static gboolean shutdown_pressed_event_cb (GtkWidget *widget, #ifdef INCLUDE_GPIO gpio_close(); #endif - if(protocol==ORIGINAL_PROTOCOL) { - old_protocol_stop(); - } else { - new_protocol_stop(); + switch(protocol) { + case ORIGINAL_PROTOCOL: + old_protocol_stop(); + break; + case NEW_PROTOCOL: + new_protocol_stop(); + break; +#ifdef LIMESDR + case LIMESDR_PROTOCOL: + lime_protocol_stop(); + break; +#endif } radioSaveState(); system("shutdown -h -P now"); @@ -358,6 +386,13 @@ static void rx_ant_cb(GtkWidget *widget, gpointer data) { set_alex_rx_antenna(ant); } +static void rx_lime_ant_cb(GtkWidget *widget, gpointer data) { + int ant=((int)data)&0xF; + BAND *band=band_get_current_band(); + band->alexRxAntenna=ant; + set_alex_rx_antenna(ant); +} + static void tx_ant_cb(GtkWidget *widget, gpointer data) { int b=((int)data)>>4; int ant=((int)data)&0xF; @@ -366,6 +401,10 @@ static void tx_ant_cb(GtkWidget *widget, gpointer data) { set_alex_tx_antenna(ant); } +static void freedv_text_changed_cb(GtkWidget *widget, gpointer data) { + strcpy(freedv_tx_text_data,gtk_entry_get_text(GTK_ENTRY(widget))); +} + static void switch_page_cb(GtkNotebook *notebook, GtkWidget *page, guint page_num, @@ -373,19 +412,21 @@ static void switch_page_cb(GtkNotebook *notebook, { int i, j; GtkWidget *child; - if(page_num==ant_id) { - if(filter_board==ALEX) { - for(i=0;ititle); - //gtk_widget_override_font(band_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(band_label); - gtk_grid_attach(GTK_GRID(ant_grid),band_label,0,i+1,1,1); - - GtkWidget *rx1_b=gtk_radio_button_new(NULL); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx1_b), band->alexRxAntenna==0); - gtk_widget_show(rx1_b); - gtk_grid_attach(GTK_GRID(ant_grid),rx1_b,1,i+1,1,1); - g_signal_connect(rx1_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+0)); - - GtkWidget *rx2_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(rx1_b)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx2_b), band->alexRxAntenna==1); - gtk_widget_show(rx2_b); - gtk_grid_attach(GTK_GRID(ant_grid),rx2_b,2,i+1,1,1); - g_signal_connect(rx2_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+1)); - - GtkWidget *rx3_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(rx2_b)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx3_b), band->alexRxAntenna==2); - gtk_widget_show(rx3_b); - gtk_grid_attach(GTK_GRID(ant_grid),rx3_b,3,i+1,1,1); - g_signal_connect(rx3_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+2)); - - GtkWidget *ext1_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(rx3_b)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ext1_b), band->alexRxAntenna==3); - gtk_widget_show(ext1_b); - gtk_grid_attach(GTK_GRID(ant_grid),ext1_b,4,i+1,1,1); - g_signal_connect(ext1_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+3)); - - GtkWidget *ext2_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(ext1_b)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ext2_b), band->alexRxAntenna==4); - gtk_widget_show(ext2_b); - gtk_grid_attach(GTK_GRID(ant_grid),ext2_b,5,i+1,1,1); - g_signal_connect(ext2_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+4)); - - GtkWidget *xvtr_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(ext2_b)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (xvtr_b), band->alexRxAntenna==5); - gtk_widget_show(xvtr_b); - gtk_grid_attach(GTK_GRID(ant_grid),xvtr_b,6,i+1,1,1); - g_signal_connect(xvtr_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+5)); - - GtkWidget *ant_band_label=gtk_label_new(band->title); - //gtk_widget_override_font(ant_band_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(ant_band_label); - gtk_grid_attach(GTK_GRID(ant_grid),ant_band_label,7,i+1,1,1); - - GtkWidget *tx1_b=gtk_radio_button_new(NULL); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tx1_b), band->alexTxAntenna==0); - gtk_widget_show(tx1_b); - gtk_grid_attach(GTK_GRID(ant_grid),tx1_b,8,i+1,1,1); - g_signal_connect(tx1_b,"pressed",G_CALLBACK(tx_ant_cb),(gpointer)((i<<4)+0)); - - GtkWidget *tx2_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(tx1_b)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tx2_b), band->alexTxAntenna==1); - gtk_widget_show(tx2_b); - gtk_grid_attach(GTK_GRID(ant_grid),tx2_b,9,i+1,1,1); - g_signal_connect(tx2_b,"pressed",G_CALLBACK(tx_ant_cb),(gpointer)((i<<4)+1)); - - GtkWidget *tx3_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(tx2_b)); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tx3_b), band->alexTxAntenna==2); - gtk_widget_show(tx3_b); - gtk_grid_attach(GTK_GRID(ant_grid),tx3_b,10,i+1,1,1); - g_signal_connect(tx3_b,"pressed",G_CALLBACK(tx_ant_cb),(gpointer)((i<<4)+2)); + for(i=0;ititle); + //gtk_widget_override_font(band_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(band_label); + gtk_grid_attach(GTK_GRID(ant_grid),band_label,0,i+1,1,1); + + GtkWidget *rx1_b=gtk_radio_button_new(NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx1_b), band->alexRxAntenna==0); + gtk_widget_show(rx1_b); + gtk_grid_attach(GTK_GRID(ant_grid),rx1_b,1,i+1,1,1); + g_signal_connect(rx1_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+0)); + + GtkWidget *rx2_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(rx1_b)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx2_b), band->alexRxAntenna==1); + gtk_widget_show(rx2_b); + gtk_grid_attach(GTK_GRID(ant_grid),rx2_b,2,i+1,1,1); + g_signal_connect(rx2_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+1)); + + GtkWidget *rx3_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(rx2_b)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx3_b), band->alexRxAntenna==2); + gtk_widget_show(rx3_b); + gtk_grid_attach(GTK_GRID(ant_grid),rx3_b,3,i+1,1,1); + g_signal_connect(rx3_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+2)); + + GtkWidget *ext1_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(rx3_b)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ext1_b), band->alexRxAntenna==3); + gtk_widget_show(ext1_b); + gtk_grid_attach(GTK_GRID(ant_grid),ext1_b,4,i+1,1,1); + g_signal_connect(ext1_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+3)); + + GtkWidget *ext2_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(ext1_b)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ext2_b), band->alexRxAntenna==4); + gtk_widget_show(ext2_b); + gtk_grid_attach(GTK_GRID(ant_grid),ext2_b,5,i+1,1,1); + g_signal_connect(ext2_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+4)); + + GtkWidget *xvtr_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(ext2_b)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (xvtr_b), band->alexRxAntenna==5); + gtk_widget_show(xvtr_b); + gtk_grid_attach(GTK_GRID(ant_grid),xvtr_b,6,i+1,1,1); + g_signal_connect(xvtr_b,"pressed",G_CALLBACK(rx_ant_cb),(gpointer)((i<<4)+5)); + + GtkWidget *ant_band_label=gtk_label_new(band->title); + //gtk_widget_override_font(ant_band_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(ant_band_label); + gtk_grid_attach(GTK_GRID(ant_grid),ant_band_label,7,i+1,1,1); + + GtkWidget *tx1_b=gtk_radio_button_new(NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tx1_b), band->alexTxAntenna==0); + gtk_widget_show(tx1_b); + gtk_grid_attach(GTK_GRID(ant_grid),tx1_b,8,i+1,1,1); + g_signal_connect(tx1_b,"pressed",G_CALLBACK(tx_ant_cb),(gpointer)((i<<4)+0)); + + GtkWidget *tx2_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(tx1_b)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tx2_b), band->alexTxAntenna==1); + gtk_widget_show(tx2_b); + gtk_grid_attach(GTK_GRID(ant_grid),tx2_b,9,i+1,1,1); + g_signal_connect(tx2_b,"pressed",G_CALLBACK(tx_ant_cb),(gpointer)((i<<4)+1)); + + GtkWidget *tx3_b=gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(tx2_b)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tx3_b), band->alexTxAntenna==2); + gtk_widget_show(tx3_b); + gtk_grid_attach(GTK_GRID(ant_grid),tx3_b,10,i+1,1,1); + g_signal_connect(tx3_b,"pressed",G_CALLBACK(tx_ant_cb),(gpointer)((i<<4)+2)); + + } + } +#ifdef LIMESDR + if(protocol==LIMESDR_PROTOCOL) { + BAND *band=band_get_current_band(); + + GtkWidget *rx1_none=gtk_radio_button_new_with_label(NULL,"RX 1: NONE"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx1_none), band->alexRxAntenna==0); + gtk_widget_show(rx1_none); + gtk_grid_attach(GTK_GRID(ant_grid),rx1_none,0,0,1,1); + g_signal_connect(rx1_none,"pressed",G_CALLBACK(rx_lime_ant_cb),(gpointer)0); + + GtkWidget *rx1_lnah=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(rx1_none),"RX1: LNAH"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx1_lnah), band->alexRxAntenna==1); + gtk_widget_show(rx1_lnah); + gtk_grid_attach(GTK_GRID(ant_grid),rx1_lnah,0,1,1,1); + g_signal_connect(rx1_lnah,"pressed",G_CALLBACK(rx_lime_ant_cb),(gpointer)+1); + + GtkWidget *rx1_lnal=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(rx1_lnah),"RX1: LNAL"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx1_lnal), band->alexRxAntenna==2); + gtk_widget_show(rx1_lnal); + gtk_grid_attach(GTK_GRID(ant_grid),rx1_lnal,0,2,1,1); + g_signal_connect(rx1_lnal,"pressed",G_CALLBACK(rx_lime_ant_cb),(gpointer)2); + + GtkWidget *rx1_lnaw=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(rx1_lnal),"RX1: LNAW"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rx1_lnaw), band->alexRxAntenna==3); + gtk_widget_show(rx1_lnaw); + gtk_grid_attach(GTK_GRID(ant_grid),rx1_lnaw,0,3,1,1); + g_signal_connect(rx1_lnaw,"pressed",G_CALLBACK(rx_lime_ant_cb),(gpointer)3); } +#endif ant_id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),ant_grid,ant_label); @@ -895,250 +971,274 @@ static gboolean menu_pressed_event_cb (GtkWidget *widget, id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),dsp_grid,dsp_label); - GtkWidget *tx_label=gtk_label_new("PA Gain"); - GtkWidget *tx_grid=gtk_grid_new(); - gtk_grid_set_row_homogeneous(GTK_GRID(tx_grid),TRUE); - gtk_grid_set_column_spacing (GTK_GRID(tx_grid),10); - - for(i=0;ititle); - //gtk_widget_override_font(band_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(band_label); - gtk_grid_attach(GTK_GRID(tx_grid),band_label,(i/6)*2,i%6,1,1); - - GtkWidget *pa_r=gtk_spin_button_new_with_range(0.0,100.0,1.0); - //gtk_widget_override_font(pa_r, pango_font_description_from_string("Arial 18")); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(pa_r),(double)band->pa_calibration); - gtk_widget_show(pa_r); - gtk_grid_attach(GTK_GRID(tx_grid),pa_r,((i/6)*2)+1,i%6,1,1); - g_signal_connect(pa_r,"value_changed",G_CALLBACK(pa_value_changed_cb),band); + if(protocol==ORIGINAL_PROTOCOL || protocol==NEW_PROTOCOL) { + GtkWidget *tx_label=gtk_label_new("PA Gain"); + GtkWidget *tx_grid=gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(tx_grid),TRUE); + gtk_grid_set_column_spacing (GTK_GRID(tx_grid),10); + + for(i=0;ititle); + //gtk_widget_override_font(band_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(band_label); + gtk_grid_attach(GTK_GRID(tx_grid),band_label,(i/6)*2,i%6,1,1); + + GtkWidget *pa_r=gtk_spin_button_new_with_range(0.0,100.0,1.0); + //gtk_widget_override_font(pa_r, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(pa_r),(double)band->pa_calibration); + gtk_widget_show(pa_r); + gtk_grid_attach(GTK_GRID(tx_grid),pa_r,((i/6)*2)+1,i%6,1,1); + g_signal_connect(pa_r,"value_changed",G_CALLBACK(pa_value_changed_cb),band); + } + + GtkWidget *tx_out_of_band_b=gtk_check_button_new_with_label("Transmit out of band"); + //gtk_widget_override_font(tx_out_of_band_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tx_out_of_band_b), tx_out_of_band); + gtk_widget_show(tx_out_of_band_b); + gtk_grid_attach(GTK_GRID(tx_grid),tx_out_of_band_b,0,7,4,1); + g_signal_connect(tx_out_of_band_b,"toggled",G_CALLBACK(tx_out_of_band_cb),NULL); + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),tx_grid,tx_label); } - GtkWidget *tx_out_of_band_b=gtk_check_button_new_with_label("Transmit out of band"); - //gtk_widget_override_font(tx_out_of_band_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tx_out_of_band_b), tx_out_of_band); - gtk_widget_show(tx_out_of_band_b); - gtk_grid_attach(GTK_GRID(tx_grid),tx_out_of_band_b,0,7,4,1); - g_signal_connect(tx_out_of_band_b,"toggled",G_CALLBACK(tx_out_of_band_cb),NULL); - - id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),tx_grid,tx_label); - - GtkWidget *cw_label=gtk_label_new("CW"); - GtkWidget *cw_grid=gtk_grid_new(); - gtk_grid_set_row_homogeneous(GTK_GRID(cw_grid),TRUE); - - GtkWidget *cw_keyer_internal_b=gtk_check_button_new_with_label("CW Internal - Speed (WPM)"); - //gtk_widget_override_font(cw_keyer_internal_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_internal_b), cw_keyer_internal); - gtk_widget_show(cw_keyer_internal_b); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_internal_b,0,0,1,1); - g_signal_connect(cw_keyer_internal_b,"toggled",G_CALLBACK(cw_keyer_internal_cb),NULL); - - GtkWidget *cw_keyer_speed_b=gtk_spin_button_new_with_range(1.0,60.0,1.0); - //gtk_widget_override_font(cw_keyer_speed_b, pango_font_description_from_string("Arial 18")); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_speed_b),(double)cw_keyer_speed); - gtk_widget_show(cw_keyer_speed_b); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_speed_b,1,0,1,1); - g_signal_connect(cw_keyer_speed_b,"value_changed",G_CALLBACK(cw_keyer_speed_value_changed_cb),NULL); - - GtkWidget *cw_breakin_b=gtk_check_button_new_with_label("CW Break In - Delay (ms)"); - //gtk_widget_override_font(cw_breakin_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_breakin_b), cw_breakin); - gtk_widget_show(cw_breakin_b); - gtk_grid_attach(GTK_GRID(cw_grid),cw_breakin_b,0,1,1,1); - g_signal_connect(cw_breakin_b,"toggled",G_CALLBACK(cw_breakin_cb),NULL); - - GtkWidget *cw_keyer_hang_time_b=gtk_spin_button_new_with_range(0.0,1000.0,1.0); - //gtk_widget_override_font(cw_keyer_hang_time_b, pango_font_description_from_string("Arial 18")); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_hang_time_b),(double)cw_keyer_hang_time); - gtk_widget_show(cw_keyer_hang_time_b); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_hang_time_b,1,1,1,1); - g_signal_connect(cw_keyer_hang_time_b,"value_changed",G_CALLBACK(cw_keyer_hang_time_value_changed_cb),NULL); + if(protocol==ORIGINAL_PROTOCOL || protocol==NEW_PROTOCOL) { + GtkWidget *cw_label=gtk_label_new("CW"); + GtkWidget *cw_grid=gtk_grid_new(); + gtk_grid_set_row_homogeneous(GTK_GRID(cw_grid),TRUE); + + GtkWidget *cw_keyer_internal_b=gtk_check_button_new_with_label("CW Internal - Speed (WPM)"); + //gtk_widget_override_font(cw_keyer_internal_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_internal_b), cw_keyer_internal); + gtk_widget_show(cw_keyer_internal_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_internal_b,0,0,1,1); + g_signal_connect(cw_keyer_internal_b,"toggled",G_CALLBACK(cw_keyer_internal_cb),NULL); + + GtkWidget *cw_keyer_speed_b=gtk_spin_button_new_with_range(1.0,60.0,1.0); + //gtk_widget_override_font(cw_keyer_speed_b, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_speed_b),(double)cw_keyer_speed); + gtk_widget_show(cw_keyer_speed_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_speed_b,1,0,1,1); + g_signal_connect(cw_keyer_speed_b,"value_changed",G_CALLBACK(cw_keyer_speed_value_changed_cb),NULL); + + GtkWidget *cw_breakin_b=gtk_check_button_new_with_label("CW Break In - Delay (ms)"); + //gtk_widget_override_font(cw_breakin_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_breakin_b), cw_breakin); + gtk_widget_show(cw_breakin_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_breakin_b,0,1,1,1); + g_signal_connect(cw_breakin_b,"toggled",G_CALLBACK(cw_breakin_cb),NULL); + + GtkWidget *cw_keyer_hang_time_b=gtk_spin_button_new_with_range(0.0,1000.0,1.0); + //gtk_widget_override_font(cw_keyer_hang_time_b, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_hang_time_b),(double)cw_keyer_hang_time); + gtk_widget_show(cw_keyer_hang_time_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_hang_time_b,1,1,1,1); + g_signal_connect(cw_keyer_hang_time_b,"value_changed",G_CALLBACK(cw_keyer_hang_time_value_changed_cb),NULL); - GtkWidget *cw_keyer_straight=gtk_radio_button_new_with_label(NULL,"CW KEYER STRAIGHT"); - //gtk_widget_override_font(cw_keyer_straight, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_straight), cw_keyer_mode==KEYER_STRAIGHT); - gtk_widget_show(cw_keyer_straight); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_straight,0,2,1,1); - g_signal_connect(cw_keyer_straight,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_STRAIGHT); - - GtkWidget *cw_keyer_mode_a=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cw_keyer_straight),"CW KEYER MODE A"); - //gtk_widget_override_font(cw_keyer_mode_a, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_mode_a), cw_keyer_mode==KEYER_MODE_A); - gtk_widget_show(cw_keyer_mode_a); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_mode_a,0,3,1,1); - g_signal_connect(cw_keyer_mode_a,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_MODE_A); - - GtkWidget *cw_keyer_mode_b=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cw_keyer_mode_a),"CW KEYER MODE B"); - //gtk_widget_override_font(cw_keyer_mode_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_mode_b), cw_keyer_mode==KEYER_MODE_B); - gtk_widget_show(cw_keyer_mode_b); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_mode_b,0,4,1,1); - g_signal_connect(cw_keyer_mode_b,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_MODE_B); - - GtkWidget *cw_keys_reversed_b=gtk_check_button_new_with_label("Keys reversed"); - //gtk_widget_override_font(cw_keys_reversed_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keys_reversed_b), cw_keys_reversed); - gtk_widget_show(cw_keys_reversed_b); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keys_reversed_b,0,5,1,1); - g_signal_connect(cw_keys_reversed_b,"toggled",G_CALLBACK(cw_keys_reversed_cb),NULL); - - GtkWidget *cw_keyer_sidetone_level_label=gtk_label_new("Sidetone Level:"); - //gtk_widget_override_font(cw_keyer_sidetone_level_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(cw_keyer_sidetone_level_label); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_sidetone_level_label,0,6,1,1); - - GtkWidget *cw_keyer_sidetone_level_b=gtk_spin_button_new_with_range(1.0,protocol==NEW_PROTOCOL?255.0:127.0,1.0); - //gtk_widget_override_font(cw_keyer_sidetone_level_b, pango_font_description_from_string("Arial 18")); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_sidetone_level_b),(double)cw_keyer_sidetone_volume); - gtk_widget_show(cw_keyer_sidetone_level_b); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_sidetone_level_b,1,6,1,1); - g_signal_connect(cw_keyer_sidetone_level_b,"value_changed",G_CALLBACK(cw_keyer_sidetone_level_value_changed_cb),NULL); - - GtkWidget *cw_keyer_sidetone_frequency_label=gtk_label_new("Sidetone Freq:"); - //gtk_widget_override_font(cw_keyer_sidetone_frequency_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(cw_keyer_sidetone_frequency_label); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_sidetone_frequency_label,0,7,1,1); - - GtkWidget *cw_keyer_sidetone_frequency_b=gtk_spin_button_new_with_range(100.0,1000.0,1.0); - //gtk_widget_override_font(cw_keyer_sidetone_frequency_b, pango_font_description_from_string("Arial 18")); - gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_sidetone_frequency_b),(double)cw_keyer_sidetone_frequency); - gtk_widget_show(cw_keyer_sidetone_frequency_b); - gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_sidetone_frequency_b,1,7,1,1); - g_signal_connect(cw_keyer_sidetone_frequency_b,"value_changed",G_CALLBACK(cw_keyer_sidetone_frequency_value_changed_cb),NULL); - - id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),cw_grid,cw_label); - - GtkWidget *oc_label=gtk_label_new("OC"); - GtkWidget *oc_grid=gtk_grid_new(); - //gtk_grid_set_row_homogeneous(GTK_GRID(oc_grid),TRUE); - gtk_grid_set_column_spacing (GTK_GRID(oc_grid),10); - - GtkWidget *band_title=gtk_label_new("Band"); - //gtk_widget_override_font(band_title, pango_font_description_from_string("Arial 18")); - gtk_widget_show(band_title); - gtk_grid_attach(GTK_GRID(oc_grid),band_title,0,0,1,1); - - GtkWidget *rx_title=gtk_label_new("Rx"); - //gtk_widget_override_font(rx_title, pango_font_description_from_string("Arial 18")); - gtk_widget_show(rx_title); - gtk_grid_attach(GTK_GRID(oc_grid),rx_title,4,0,1,1); - - GtkWidget *tx_title=gtk_label_new("Tx"); - //gtk_widget_override_font(tx_title, pango_font_description_from_string("Arial 18")); - gtk_widget_show(tx_title); - gtk_grid_attach(GTK_GRID(oc_grid),tx_title,11,0,1,1); - - GtkWidget *tune_title=gtk_label_new("Tune (ORed with TX)"); - //gtk_widget_override_font(tune_title, pango_font_description_from_string("Arial 18")); - gtk_widget_show(tune_title); - gtk_grid_attach(GTK_GRID(oc_grid),tune_title,18,0,2,1); - - for(i=1;i<8;i++) { - char oc_id[8]; - sprintf(oc_id,"%d",i); - GtkWidget *oc_rx_title=gtk_label_new(oc_id); - //gtk_widget_override_font(oc_rx_title, pango_font_description_from_string("Arial 18")); - gtk_widget_show(oc_rx_title); - gtk_grid_attach(GTK_GRID(oc_grid),oc_rx_title,i,1,1,1); - GtkWidget *oc_tx_title=gtk_label_new(oc_id); - //gtk_widget_override_font(oc_tx_title, pango_font_description_from_string("Arial 18")); - gtk_widget_show(oc_tx_title); - gtk_grid_attach(GTK_GRID(oc_grid),oc_tx_title,i+7,1,1,1); + GtkWidget *cw_keyer_straight=gtk_radio_button_new_with_label(NULL,"CW KEYER STRAIGHT"); + //gtk_widget_override_font(cw_keyer_straight, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_straight), cw_keyer_mode==KEYER_STRAIGHT); + gtk_widget_show(cw_keyer_straight); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_straight,0,2,1,1); + g_signal_connect(cw_keyer_straight,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_STRAIGHT); + + GtkWidget *cw_keyer_mode_a=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cw_keyer_straight),"CW KEYER MODE A"); + //gtk_widget_override_font(cw_keyer_mode_a, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_mode_a), cw_keyer_mode==KEYER_MODE_A); + gtk_widget_show(cw_keyer_mode_a); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_mode_a,0,3,1,1); + g_signal_connect(cw_keyer_mode_a,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_MODE_A); + + GtkWidget *cw_keyer_mode_b=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(cw_keyer_mode_a),"CW KEYER MODE B"); + //gtk_widget_override_font(cw_keyer_mode_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keyer_mode_b), cw_keyer_mode==KEYER_MODE_B); + gtk_widget_show(cw_keyer_mode_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_mode_b,0,4,1,1); + g_signal_connect(cw_keyer_mode_b,"pressed",G_CALLBACK(cw_keyer_mode_cb),(gpointer *)KEYER_MODE_B); + + GtkWidget *cw_keys_reversed_b=gtk_check_button_new_with_label("Keys reversed"); + //gtk_widget_override_font(cw_keys_reversed_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (cw_keys_reversed_b), cw_keys_reversed); + gtk_widget_show(cw_keys_reversed_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keys_reversed_b,0,5,1,1); + g_signal_connect(cw_keys_reversed_b,"toggled",G_CALLBACK(cw_keys_reversed_cb),NULL); + + GtkWidget *cw_keyer_sidetone_level_label=gtk_label_new("Sidetone Level:"); + //gtk_widget_override_font(cw_keyer_sidetone_level_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(cw_keyer_sidetone_level_label); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_sidetone_level_label,0,6,1,1); + + GtkWidget *cw_keyer_sidetone_level_b=gtk_spin_button_new_with_range(1.0,protocol==NEW_PROTOCOL?255.0:127.0,1.0); + //gtk_widget_override_font(cw_keyer_sidetone_level_b, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_sidetone_level_b),(double)cw_keyer_sidetone_volume); + gtk_widget_show(cw_keyer_sidetone_level_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_sidetone_level_b,1,6,1,1); + g_signal_connect(cw_keyer_sidetone_level_b,"value_changed",G_CALLBACK(cw_keyer_sidetone_level_value_changed_cb),NULL); + + GtkWidget *cw_keyer_sidetone_frequency_label=gtk_label_new("Sidetone Freq:"); + //gtk_widget_override_font(cw_keyer_sidetone_frequency_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(cw_keyer_sidetone_frequency_label); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_sidetone_frequency_label,0,7,1,1); + + GtkWidget *cw_keyer_sidetone_frequency_b=gtk_spin_button_new_with_range(100.0,1000.0,1.0); + //gtk_widget_override_font(cw_keyer_sidetone_frequency_b, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(cw_keyer_sidetone_frequency_b),(double)cw_keyer_sidetone_frequency); + gtk_widget_show(cw_keyer_sidetone_frequency_b); + gtk_grid_attach(GTK_GRID(cw_grid),cw_keyer_sidetone_frequency_b,1,7,1,1); + g_signal_connect(cw_keyer_sidetone_frequency_b,"value_changed",G_CALLBACK(cw_keyer_sidetone_frequency_value_changed_cb),NULL); + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),cw_grid,cw_label); + } + + if(protocol==ORIGINAL_PROTOCOL || protocol==NEW_PROTOCOL) { + GtkWidget *oc_label=gtk_label_new("OC"); + GtkWidget *oc_grid=gtk_grid_new(); + //gtk_grid_set_row_homogeneous(GTK_GRID(oc_grid),TRUE); + gtk_grid_set_column_spacing (GTK_GRID(oc_grid),10); + + GtkWidget *band_title=gtk_label_new("Band"); + //gtk_widget_override_font(band_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(band_title); + gtk_grid_attach(GTK_GRID(oc_grid),band_title,0,0,1,1); + + GtkWidget *rx_title=gtk_label_new("Rx"); + //gtk_widget_override_font(rx_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(rx_title); + gtk_grid_attach(GTK_GRID(oc_grid),rx_title,4,0,1,1); + + GtkWidget *tx_title=gtk_label_new("Tx"); + //gtk_widget_override_font(tx_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(tx_title); + gtk_grid_attach(GTK_GRID(oc_grid),tx_title,11,0,1,1); + + GtkWidget *tune_title=gtk_label_new("Tune (ORed with TX)"); + //gtk_widget_override_font(tune_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(tune_title); + gtk_grid_attach(GTK_GRID(oc_grid),tune_title,18,0,2,1); + + for(i=1;i<8;i++) { + char oc_id[8]; + sprintf(oc_id,"%d",i); + GtkWidget *oc_rx_title=gtk_label_new(oc_id); + //gtk_widget_override_font(oc_rx_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(oc_rx_title); + gtk_grid_attach(GTK_GRID(oc_grid),oc_rx_title,i,1,1,1); + GtkWidget *oc_tx_title=gtk_label_new(oc_id); + //gtk_widget_override_font(oc_tx_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(oc_tx_title); + gtk_grid_attach(GTK_GRID(oc_grid),oc_tx_title,i+7,1,1,1); /* - GtkWidget *oc_tune_title=gtk_label_new(oc_id); - //gtk_widget_override_font(oc_tune_title, pango_font_description_from_string("Arial 18")); - gtk_widget_show(oc_tune_title); - gtk_grid_attach(GTK_GRID(oc_grid),oc_tune_title,i+14,1,1,1); + GtkWidget *oc_tune_title=gtk_label_new(oc_id); + //gtk_widget_override_font(oc_tune_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(oc_tune_title); + gtk_grid_attach(GTK_GRID(oc_grid),oc_tune_title,i+14,1,1,1); */ } - for(i=0;ititle); + //gtk_widget_override_font(band_label, pango_font_description_from_string("Arial 18")); + gtk_widget_show(band_label); + gtk_grid_attach(GTK_GRID(oc_grid),band_label,0,i+2,1,1); + + int mask; + for(j=1;j<8;j++) { + mask=0x01<OCrx&mask)==mask); + gtk_widget_show(oc_rx_b); + gtk_grid_attach(GTK_GRID(oc_grid),oc_rx_b,j,i+2,1,1); + g_signal_connect(oc_rx_b,"toggled",G_CALLBACK(oc_rx_cb),(gpointer)(j+(i<<4))); + + GtkWidget *oc_tx_b=gtk_check_button_new(); + //gtk_widget_override_font(oc_tx_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (oc_tx_b), (band->OCtx&mask)==mask); + gtk_widget_show(oc_tx_b); + gtk_grid_attach(GTK_GRID(oc_grid),oc_tx_b,j+7,i+2,1,1); + g_signal_connect(oc_tx_b,"toggled",G_CALLBACK(oc_tx_cb),(gpointer)(j+(i<<4))); + +/* + GtkWidget *oc_tune_b=gtk_check_button_new(); + //gtk_widget_override_font(oc_tune_b, pango_font_description_from_string("Arial 18")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (oc_tune_b), (band->OCtune&mask)==mask); + gtk_widget_show(oc_tune_b); + gtk_grid_attach(GTK_GRID(oc_grid),oc_tune_b,j+14,i+2,1,1); + g_signal_connect(oc_tune_b,"toggled",G_CALLBACK(oc_tune_cb),(gpointer)(j+(i<<4))); +*/ + } + } - GtkWidget *band_label=gtk_label_new(band->title); - //gtk_widget_override_font(band_label, pango_font_description_from_string("Arial 18")); - gtk_widget_show(band_label); - gtk_grid_attach(GTK_GRID(oc_grid),band_label,0,i+2,1,1); int mask; for(j=1;j<8;j++) { + char oc_id[8]; + sprintf(oc_id,"%d",j); + GtkWidget *oc_tune_title=gtk_label_new(oc_id); + //gtk_widget_override_font(oc_tune_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(oc_tune_title); + gtk_grid_attach(GTK_GRID(oc_grid),oc_tune_title,18,j,1,1); + mask=0x01<OCrx&mask)==mask); - gtk_widget_show(oc_rx_b); - gtk_grid_attach(GTK_GRID(oc_grid),oc_rx_b,j,i+2,1,1); - g_signal_connect(oc_rx_b,"toggled",G_CALLBACK(oc_rx_cb),(gpointer)(j+(i<<4))); - - GtkWidget *oc_tx_b=gtk_check_button_new(); - //gtk_widget_override_font(oc_tx_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (oc_tx_b), (band->OCtx&mask)==mask); - gtk_widget_show(oc_tx_b); - gtk_grid_attach(GTK_GRID(oc_grid),oc_tx_b,j+7,i+2,1,1); - g_signal_connect(oc_tx_b,"toggled",G_CALLBACK(oc_tx_cb),(gpointer)(j+(i<<4))); - -/* GtkWidget *oc_tune_b=gtk_check_button_new(); //gtk_widget_override_font(oc_tune_b, pango_font_description_from_string("Arial 18")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (oc_tune_b), (band->OCtune&mask)==mask); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (oc_tune_b), (OCtune&mask)==mask); gtk_widget_show(oc_tune_b); - gtk_grid_attach(GTK_GRID(oc_grid),oc_tune_b,j+14,i+2,1,1); - g_signal_connect(oc_tune_b,"toggled",G_CALLBACK(oc_tune_cb),(gpointer)(j+(i<<4))); -*/ + gtk_grid_attach(GTK_GRID(oc_grid),oc_tune_b,19,j,1,1); + g_signal_connect(oc_tune_b,"toggled",G_CALLBACK(oc_tune_cb),(gpointer)j); } + + GtkWidget *oc_full_tune_time_title=gtk_label_new("Full Tune(ms):"); + //gtk_widget_override_font(oc_full_tune_time_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(oc_full_tune_time_title); + gtk_grid_attach(GTK_GRID(oc_grid),oc_full_tune_time_title,18,j,2,1); + j++; + + GtkWidget *oc_full_tune_time_b=gtk_spin_button_new_with_range(0.0,9999.0,1.0); + //gtk_widget_override_font(oc_full_tune_time_b, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(oc_full_tune_time_b),(double)OCfull_tune_time); + gtk_widget_show(oc_full_tune_time_b); + gtk_grid_attach(GTK_GRID(oc_grid),oc_full_tune_time_b,18,j,2,1); + g_signal_connect(oc_full_tune_time_b,"value_changed",G_CALLBACK(oc_full_tune_time_cb),NULL); + j++; + + GtkWidget *oc_memory_tune_time_title=gtk_label_new("Memory Tune(ms):"); + //gtk_widget_override_font(oc_memory_tune_time_title, pango_font_description_from_string("Arial 18")); + gtk_widget_show(oc_memory_tune_time_title); + gtk_grid_attach(GTK_GRID(oc_grid),oc_memory_tune_time_title,18,j,2,1); + j++; + + GtkWidget *oc_memory_tune_time_b=gtk_spin_button_new_with_range(0.0,9999.0,1.0); + //gtk_widget_override_font(oc_memory_tune_time_b, pango_font_description_from_string("Arial 18")); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(oc_memory_tune_time_b),(double)OCmemory_tune_time); + gtk_widget_show(oc_memory_tune_time_b); + gtk_grid_attach(GTK_GRID(oc_grid),oc_memory_tune_time_b,18,j,2,1); + g_signal_connect(oc_memory_tune_time_b,"value_changed",G_CALLBACK(oc_memory_tune_time_cb),NULL); + j++; + + id=gtk_notebook_append_page(GTK_NOTEBOOK(notebook),oc_grid,oc_label); } - int mask; - for(j=1;j<8;j++) { - char oc_id[8]; - sprintf(oc_id,"%d",j); - GtkWidget *oc_tune_title=gtk_label_new(oc_id); - //gtk_widget_override_font(oc_tune_title, pango_font_description_from_string("Arial 18")); - gtk_widget_show(oc_tune_title); - gtk_grid_attach(GTK_GRID(oc_grid),oc_tune_title,18,j,1,1); - - mask=0x01< #include "meter.h" +#ifdef FREEDV +#include "radio.h" +#include "mode.h" +#include "freedv.h" +#endif static GtkWidget *meter; static cairo_surface_t *meter_surface = NULL; @@ -201,6 +206,21 @@ void meter_update(int meter_type,double value,double reverse,double exciter) { sprintf(sf,"%d dBm",(int)level); cairo_move_to(cr, text_location, 45); cairo_show_text(cr, sf); + +#ifdef FREEDV + if(mode==modeFREEDV) { + if(freedv_sync) { + cairo_set_source_rgb(cr, 0, 1, 0); + } else { + cairo_set_source_rgb(cr, 1, 0, 0); + } + cairo_set_font_size(cr, 16); + sprintf(sf,"SNR: %3.2f",freedv_snr); + cairo_move_to(cr, text_location, 20); + cairo_show_text(cr, sf); + } +#endif + break; case POWER: // value is Watts diff --git a/mode.c b/mode.c index 8bf7706..6450963 100644 --- a/mode.c +++ b/mode.c @@ -17,5 +17,21 @@ * */ -char *mode_string[]={"LSB","USB","DSB","CWL","CWU","FMN","AM","DIGU","SPEC","DIGL","SAM","DRM","FREEDV"}; +char *mode_string[]={ + "LSB" + ,"USB" + ,"DSB" + ,"CWL" + ,"CWU" + ,"FMN" + ,"AM" + ,"DIGU" + ,"SPEC" + ,"DIGL" + ,"SAM" + ,"DRM" +#ifdef FREEDV + ,"FREEDV" +#endif +}; diff --git a/mode.h b/mode.h index 6176396..71d9dca 100644 --- a/mode.h +++ b/mode.h @@ -29,9 +29,12 @@ #define modeDIGL 9 #define modeSAM 10 #define modeDRM 11 +#ifdef FREEDV #define modeFREEDV 12 - #define MODES 13 +#else +#define MODES 12 +#endif int mode; diff --git a/new_discovery.c b/new_discovery.c index dc4a8f3..1d3658a 100644 --- a/new_discovery.c +++ b/new_discovery.c @@ -57,14 +57,14 @@ void print_device(int i) { discovered[i].device, discovered[i].software_version, discovered[i].status, - inet_ntoa(discovered[i].address.sin_addr), - discovered[i].mac_address[0], - discovered[i].mac_address[1], - discovered[i].mac_address[2], - discovered[i].mac_address[3], - discovered[i].mac_address[4], - discovered[i].mac_address[5], - discovered[i].interface_name); + inet_ntoa(discovered[i].info.network.address.sin_addr), + discovered[i].info.network.mac_address[0], + discovered[i].info.network.mac_address[1], + discovered[i].info.network.mac_address[2], + discovered[i].info.network.mac_address[3], + discovered[i].info.network.mac_address[4], + discovered[i].info.network.mac_address[5], + discovered[i].info.network.interface_name); } void new_discovery() { @@ -240,28 +240,28 @@ void* new_discover_receive_thread(void* arg) { } discovered[devices].software_version=buffer[13]&0xFF; for(i=0;i<6;i++) { - discovered[devices].mac_address[i]=buffer[i+5]; + discovered[devices].info.network.mac_address[i]=buffer[i+5]; } discovered[devices].status=status; - memcpy((void*)&discovered[devices].address,(void*)&addr,sizeof(addr)); - discovered[devices].address_length=sizeof(addr); - memcpy((void*)&discovered[devices].interface_address,(void*)&interface_addr,sizeof(interface_addr)); - memcpy((void*)&discovered[devices].interface_netmask,(void*)&interface_netmask,sizeof(interface_netmask)); - discovered[devices].interface_length=sizeof(interface_addr); - strcpy(discovered[devices].interface_name,interface_name); + memcpy((void*)&discovered[devices].info.network.address,(void*)&addr,sizeof(addr)); + discovered[devices].info.network.address_length=sizeof(addr); + memcpy((void*)&discovered[devices].info.network.interface_address,(void*)&interface_addr,sizeof(interface_addr)); + memcpy((void*)&discovered[devices].info.network.interface_netmask,(void*)&interface_netmask,sizeof(interface_netmask)); + discovered[devices].info.network.interface_length=sizeof(interface_addr); + strcpy(discovered[devices].info.network.interface_name,interface_name); fprintf(stderr,"new_discover: found protocol=%d device=%d software_version=%d status=%d address=%s (%02X:%02X:%02X:%02X:%02X:%02X) on %s\n", discovered[devices].protocol, discovered[devices].device, discovered[devices].software_version, discovered[devices].status, - inet_ntoa(discovered[devices].address.sin_addr), - discovered[devices].mac_address[0], - discovered[devices].mac_address[1], - discovered[devices].mac_address[2], - discovered[devices].mac_address[3], - discovered[devices].mac_address[4], - discovered[devices].mac_address[5], - discovered[devices].interface_name); + inet_ntoa(discovered[devices].info.network.address.sin_addr), + discovered[devices].info.network.mac_address[0], + discovered[devices].info.network.mac_address[1], + discovered[devices].info.network.mac_address[2], + discovered[devices].info.network.mac_address[3], + discovered[devices].info.network.mac_address[4], + discovered[devices].info.network.mac_address[5], + discovered[devices].info.network.interface_name); devices++; } } diff --git a/new_protocol.c b/new_protocol.c index a308cf7..97d40e8 100644 --- a/new_protocol.c +++ b/new_protocol.c @@ -647,37 +647,37 @@ fprintf(stderr,"outputsamples=%d\n", outputsamples); setsockopt(data_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); // bind to the interface - if(bind(data_socket,(struct sockaddr*)&d->interface_address,d->interface_length)<0) { + if(bind(data_socket,(struct sockaddr*)&d->info.network.interface_address,d->info.network.interface_length)<0) { fprintf(stderr,"metis: bind socket failed for data_socket: receiver=%d\n",receiver); exit(-1); } - memcpy(&base_addr,&d->address,d->address_length); - base_addr_length=d->address_length; + memcpy(&base_addr,&d->info.network.address,d->info.network.address_length); + base_addr_length=d->info.network.address_length; base_addr.sin_port=htons(GENERAL_REGISTERS_FROM_HOST_PORT); - memcpy(&receiver_addr,&d->address,d->address_length); - receiver_addr_length=d->address_length; + memcpy(&receiver_addr,&d->info.network.address,d->info.network.address_length); + receiver_addr_length=d->info.network.address_length; receiver_addr.sin_port=htons(RECEIVER_SPECIFIC_REGISTERS_FROM_HOST_PORT); - memcpy(&transmitter_addr,&d->address,d->address_length); - transmitter_addr_length=d->address_length; + memcpy(&transmitter_addr,&d->info.network.address,d->info.network.address_length); + transmitter_addr_length=d->info.network.address_length; transmitter_addr.sin_port=htons(TRANSMITTER_SPECIFIC_REGISTERS_FROM_HOST_PORT); - memcpy(&high_priority_addr,&d->address,d->address_length); - high_priority_addr_length=d->address_length; + memcpy(&high_priority_addr,&d->info.network.address,d->info.network.address_length); + high_priority_addr_length=d->info.network.address_length; high_priority_addr.sin_port=htons(HIGH_PRIORITY_FROM_HOST_PORT); - memcpy(&audio_addr,&d->address,d->address_length); - audio_addr_length=d->address_length; + memcpy(&audio_addr,&d->info.network.address,d->info.network.address_length); + audio_addr_length=d->info.network.address_length; audio_addr.sin_port=htons(AUDIO_FROM_HOST_PORT); - memcpy(&iq_addr,&d->address,d->address_length); - iq_addr_length=d->address_length; + memcpy(&iq_addr,&d->info.network.address,d->info.network.address_length); + iq_addr_length=d->info.network.address_length; iq_addr.sin_port=htons(TX_IQ_FROM_HOST_PORT); - memcpy(&data_addr,&d->address,d->address_length); - data_addr_length=d->address_length; + memcpy(&data_addr,&d->info.network.address,d->info.network.address_length); + data_addr_length=d->info.network.address_length; data_addr.sin_port=htons(RX_IQ_TO_HOST_PORT+receiver); samples=0; diff --git a/new_protocol_programmer.c b/new_protocol_programmer.c index 728970c..0692a75 100644 --- a/new_protocol_programmer.c +++ b/new_protocol_programmer.c @@ -69,8 +69,8 @@ void new_protocol_programmer(char *filename ) { DISCOVERED* d=&discovered[selected_device]; - memcpy(&program_addr,&d->address,d->address_length); - program_addr_length=d->address_length; + memcpy(&program_addr,&d->info.network.address,d->info.network.address_length); + program_addr_length=d->info.network.address_length; program_addr.sin_port=htons(PROGRAMMING_FROM_HOST_PORT); FILE *fp; diff --git a/old_discovery.c b/old_discovery.c index 7867f6e..6a22fe1 100644 --- a/old_discovery.c +++ b/old_discovery.c @@ -72,7 +72,8 @@ static void discover(struct ifaddrs* iface) { // bind to this interface and the discovery port interface_addr.sin_family = AF_INET; interface_addr.sin_addr.s_addr = sa->sin_addr.s_addr; - interface_addr.sin_port = htons(DISCOVERY_PORT*2); + //interface_addr.sin_port = htons(DISCOVERY_PORT*2); + interface_addr.sin_port = htons(0); // system assigned port if(bind(discovery_socket,(struct sockaddr*)&interface_addr,sizeof(interface_addr))<0) { perror("discover: bind socket failed for discovery_socket\n"); exit(-1); @@ -182,27 +183,27 @@ fprintf(stderr,"discover_receive_thread\n"); } discovered[devices].software_version=buffer[9]&0xFF; for(i=0;i<6;i++) { - discovered[devices].mac_address[i]=buffer[i+3]; + discovered[devices].info.network.mac_address[i]=buffer[i+3]; } discovered[devices].status=status; - memcpy((void*)&discovered[devices].address,(void*)&addr,sizeof(addr)); - discovered[devices].address_length=sizeof(addr); - memcpy((void*)&discovered[devices].interface_address,(void*)&interface_addr,sizeof(interface_addr)); - memcpy((void*)&discovered[devices].interface_netmask,(void*)&interface_netmask,sizeof(interface_netmask)); - discovered[devices].interface_length=sizeof(interface_addr); - strcpy(discovered[devices].interface_name,interface_name); + memcpy((void*)&discovered[devices].info.network.address,(void*)&addr,sizeof(addr)); + discovered[devices].info.network.address_length=sizeof(addr); + memcpy((void*)&discovered[devices].info.network.interface_address,(void*)&interface_addr,sizeof(interface_addr)); + memcpy((void*)&discovered[devices].info.network.interface_netmask,(void*)&interface_netmask,sizeof(interface_netmask)); + discovered[devices].info.network.interface_length=sizeof(interface_addr); + strcpy(discovered[devices].info.network.interface_name,interface_name); fprintf(stderr,"discovery: found device=%d software_version=%d status=%d address=%s (%02X:%02X:%02X:%02X:%02X:%02X) on %s\n", discovered[devices].device, discovered[devices].software_version, discovered[devices].status, - inet_ntoa(discovered[devices].address.sin_addr), - discovered[devices].mac_address[0], - discovered[devices].mac_address[1], - discovered[devices].mac_address[2], - discovered[devices].mac_address[3], - discovered[devices].mac_address[4], - discovered[devices].mac_address[5], - discovered[devices].interface_name); + inet_ntoa(discovered[devices].info.network.address.sin_addr), + discovered[devices].info.network.mac_address[0], + discovered[devices].info.network.mac_address[1], + discovered[devices].info.network.mac_address[2], + discovered[devices].info.network.mac_address[3], + discovered[devices].info.network.mac_address[4], + discovered[devices].info.network.mac_address[5], + discovered[devices].info.network.interface_name); devices++; } } @@ -240,14 +241,14 @@ fprintf(stderr,"old_discovery\n"); discovered[i].device, discovered[i].software_version, discovered[i].status, - inet_ntoa(discovered[i].address.sin_addr), - discovered[i].mac_address[0], - discovered[i].mac_address[1], - discovered[i].mac_address[2], - discovered[i].mac_address[3], - discovered[i].mac_address[4], - discovered[i].mac_address[5], - discovered[i].interface_name); + inet_ntoa(discovered[i].info.network.address.sin_addr), + discovered[i].info.network.mac_address[0], + discovered[i].info.network.mac_address[1], + discovered[i].info.network.mac_address[2], + discovered[i].info.network.mac_address[3], + discovered[i].info.network.mac_address[4], + discovered[i].info.network.mac_address[5], + discovered[i].info.network.interface_name); } } diff --git a/old_protocol.c b/old_protocol.c index b33f5b7..88a3239 100644 --- a/old_protocol.c +++ b/old_protocol.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include "band.h" #include "channel.h" @@ -123,6 +125,8 @@ static int running; static long ep4_sequence; static int samples=0; +static int freedv_samples=0; +static int freedv_divisor=6; //static float left_input_buffer[BUFFER_SIZE]; //static float right_input_buffer[BUFFER_SIZE]; @@ -170,6 +174,8 @@ static sem_t frequency_changed_sem; static int metis_write(unsigned char ep,char* buffer,int length); static void metis_start_stop(int command); static void metis_send_buffer(char* buffer,int length); +static void full_rx_buffer(); +static void full_tx_buffer(); void schedule_frequency_changed() { //fprintf(stderr,"old_protocol: schedule_frequency_changed\n"); @@ -208,15 +214,19 @@ void old_protocol_init(int rx,int pixels) { switch(sample_rate) { case 48000: output_buffer_size=OUTPUT_BUFFER_SIZE; + freedv_divisor=6; break; case 96000: output_buffer_size=OUTPUT_BUFFER_SIZE/2; + freedv_divisor=12; break; case 192000: output_buffer_size=OUTPUT_BUFFER_SIZE/4; + freedv_divisor=24; break; case 384000: output_buffer_size=OUTPUT_BUFFER_SIZE/8; + freedv_divisor=48; break; default: fprintf(stderr,"Invalid sample rate: %d. Defaulting to 48K.\n",sample_rate); @@ -250,7 +260,7 @@ static void start_receive_thread() { int rc; struct hostent *h; - fprintf(stderr,"old_protocol starting receive thread\n"); + fprintf(stderr,"old_protocol starting receive thread: buffer_size=%d output_buffer_size=%d\n",buffer_size,output_buffer_size); data_socket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); if(data_socket<0) { @@ -262,13 +272,13 @@ static void start_receive_thread() { setsockopt(data_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); // bind to the interface - if(bind(data_socket,(struct sockaddr*)&d->interface_address,d->interface_length)<0) { + if(bind(data_socket,(struct sockaddr*)&d->info.network.interface_address,d->info.network.interface_length)<0) { perror("old_protocol: bind socket failed for data_socket\n"); exit(-1); } - memcpy(&data_addr,&d->address,d->address_length); - data_addr_length=d->address_length; + memcpy(&data_addr,&d->info.network.address,d->info.network.address_length); + data_addr_length=d->info.network.address_length; data_addr.sin_port=htons(DATA_PORT); rc=pthread_create(&receive_thread_id,NULL,receive_thread,NULL); @@ -426,16 +436,7 @@ static void process_ozy_input_buffer(char *buffer) { right_sample += (int)((unsigned char)buffer[b++]); mic_sample = (int)((signed char) buffer[b++]) << 8; mic_sample += (int)((unsigned char)buffer[b++]); -/* - left_sample = ((int)buffer[b++]) << 16; - left_sample = (((unsigned char)buffer[b++]) << 8) | left_sample; - left_sample = ((unsigned char)buffer[b++]) | left_sample; - right_sample = ((int)buffer[b++]) << 16; - right_sample = (((unsigned char)buffer[b++]) << 8) | right_sample; - right_sample = ((unsigned char)buffer[b++]) | right_sample; - mic_sample = ((int)buffer[b++]) << 8; - mic_sample = ((unsigned char)buffer[b++]) | mic_sample; -*/ + left_sample_float=(float)left_sample/8388607.0; // 24 bit sample 2^23-1 right_sample_float=(float)right_sample/8388607.0; // 24 bit sample 2^23-1 mic_sample_float=(float)mic_sample/32767.0f; // 16 bit sample 2^16-1 @@ -444,9 +445,30 @@ static void process_ozy_input_buffer(char *buffer) { if(isTransmitting()) { #ifdef FREEDV if(mode==modeFREEDV) { - int modem_samples=mod_sample_freedv(mic_sample); - if(modem_samples!=0) { - + if(freedv_samples==0) { + int modem_samples=mod_sample_freedv(mic_sample); + if(modem_samples!=0) { + int s; + for(s=0;sdevice!=DEVICE_METIS || atlas_penelope) { - if(tune) { - gain=65535.0*255.0/(double)tune_drive; - } else { - gain=65535.0*255.0/(double)drive; - } - } else { - gain=65535.0; - } - for(j=0;j>8; - output_buffer[output_buffer_index++]=left_rx_sample; - output_buffer[output_buffer_index++]=right_rx_sample>>8; - output_buffer[output_buffer_index++]=right_rx_sample; - output_buffer[output_buffer_index++]=left_tx_sample>>8; - output_buffer[output_buffer_index++]=left_tx_sample; - output_buffer[output_buffer_index++]=right_tx_sample>>8; - output_buffer[output_buffer_index++]=right_tx_sample; - if(output_buffer_index>=OZY_BUFFER_SIZE) { - ozy_send_buffer(); - output_buffer_index=8; - } - } - } else { - // process the input - fexchange0(CHANNEL_RX0, iqinputbuffer, audiooutputbuffer, &error); - if(error!=0) { - fprintf(stderr,"fexchange2 (CHANNEL_RX0) returned error: %d\n", error); - } - Spectrum0(1, CHANNEL_RX0, 0, 0, iqinputbuffer); + fprintf(stderr,"%s: process_ozy_input_buffer: did not find sync\n", + asctime(gmt)); + exit(1); + } +} +static void full_rx_buffer() { + int j; + int error; #ifdef FREEDV - if(mode==modeFREEDV) { - int demod_samples; - int increment=6; // 48000 to 8000 - for(j=0;j>8; - output_buffer[output_buffer_index++]=left_rx_sample; - output_buffer[output_buffer_index++]=right_rx_sample>>8; - output_buffer[output_buffer_index++]=right_rx_sample; - output_buffer[output_buffer_index++]=left_tx_sample>>8; - output_buffer[output_buffer_index++]=left_tx_sample; - output_buffer[output_buffer_index++]=right_tx_sample>>8; - output_buffer[output_buffer_index++]=right_tx_sample; - if(output_buffer_index>=OZY_BUFFER_SIZE) { - ozy_send_buffer(); - output_buffer_index=8; - } - } - } + if(mode==modeFREEDV) { + // process the input + fexchange0(CHANNEL_RX0, iqinputbuffer, audiooutputbuffer, &error); + if(error!=0) { + fprintf(stderr,"fexchange2 (CHANNEL_RX0) returned error: %d\n", error); + } + Spectrum0(1, CHANNEL_RX0, 0, 0, iqinputbuffer); + + int demod_samples; + for(j=0;j>8; output_buffer[output_buffer_index++]=left_rx_sample; output_buffer[output_buffer_index++]=right_rx_sample>>8; output_buffer[output_buffer_index++]=right_rx_sample; + left_tx_sample=right_tx_sample=0; output_buffer[output_buffer_index++]=left_tx_sample>>8; - output_buffer[output_buffer_index++]=left_tx_sample; - output_buffer[output_buffer_index++]=right_tx_sample>>8; - output_buffer[output_buffer_index++]=right_tx_sample; - if(output_buffer_index>=OZY_BUFFER_SIZE) { - ozy_send_buffer(); - output_buffer_index=8; - } + output_buffer[output_buffer_index++]=left_tx_sample; + output_buffer[output_buffer_index++]=right_tx_sample>>8; + output_buffer[output_buffer_index++]=right_tx_sample; + if(output_buffer_index>=OZY_BUFFER_SIZE) { + ozy_send_buffer(); + output_buffer_index=8; + } } -#ifdef FREEDV } -#endif } - samples=0; + } + freedv_samples++; + if(freedv_samples==freedv_divisor) { + freedv_samples=0; } } } else { - time_t t; - struct tm* gmt; - time(&t); - gmt=gmtime(&t); - - fprintf(stderr,"%s: process_ozy_input_buffer: did not find sync\n", - asctime(gmt)); - exit(1); +#endif + // process the input + fexchange0(CHANNEL_RX0, iqinputbuffer, audiooutputbuffer, &error); + if(error!=0) { + fprintf(stderr,"fexchange2 (CHANNEL_RX0) returned error: %d\n", error); + } + Spectrum0(1, CHANNEL_RX0, 0, 0, iqinputbuffer); + for(j=0;j>8; + output_buffer[output_buffer_index++]=left_rx_sample; + output_buffer[output_buffer_index++]=right_rx_sample>>8; + output_buffer[output_buffer_index++]=right_rx_sample; + output_buffer[output_buffer_index++]=left_tx_sample>>8; + output_buffer[output_buffer_index++]=left_tx_sample; + output_buffer[output_buffer_index++]=right_tx_sample>>8; + output_buffer[output_buffer_index++]=right_tx_sample; + if(output_buffer_index>=OZY_BUFFER_SIZE) { + ozy_send_buffer(); + output_buffer_index=8; + } + } +#ifdef FREEDV } +#endif } +static void full_tx_buffer() { + int j; + int error; + double gain; + if(tune) { + double tunefrequency = (double)((filterHigh - filterLow) / 2); + phase=sineWave(micinputbuffer, BUFFER_SIZE, phase, (float)tunefrequency); + } + // process the output + fexchange0(CHANNEL_TX, micinputbuffer, micoutputbuffer, &error); + if(error!=0) { + fprintf(stderr,"fexchange0 (CHANNEL_TX) returned error: %d\n", error); + } + Spectrum0(1, CHANNEL_TX, 0, 0, micoutputbuffer); + if(d->device!=DEVICE_METIS || atlas_penelope) { + if(tune) { + gain=65535.0*255.0/(double)tune_drive; + } else { + gain=65535.0*255.0/(double)drive; + } + } else { + gain=65535.0; + } + for(j=0;j>8; + output_buffer[output_buffer_index++]=left_rx_sample; + output_buffer[output_buffer_index++]=right_rx_sample>>8; + output_buffer[output_buffer_index++]=right_rx_sample; + output_buffer[output_buffer_index++]=left_tx_sample>>8; + output_buffer[output_buffer_index++]=left_tx_sample; + output_buffer[output_buffer_index++]=right_tx_sample>>8; + output_buffer[output_buffer_index++]=right_tx_sample; + if(output_buffer_index>=OZY_BUFFER_SIZE) { + ozy_send_buffer(); + output_buffer_index=8; + } + } +} /* static void process_bandscope_buffer(char *buffer) { } diff --git a/panadapter.c b/panadapter.c index 95490b8..fbe109b 100644 --- a/panadapter.c +++ b/panadapter.c @@ -29,6 +29,10 @@ #include "radio.h" #include "panadapter.h" #include "vfo.h" +#ifdef FREEDV +#include "mode.h" +#include "freedv.h" +#endif static GtkWidget *panadapter; static cairo_surface_t *panadapter_surface = NULL; @@ -124,7 +128,7 @@ panadapter_button_release_event_cb (GtkWidget *widget, vfo_move((int)((float)(x-last_x)*hz_per_pixel)); } else { // move to this frequency - vfo_move((int)((float)(x-(display_width/2))*hz_per_pixel)); + vfo_move_to((int)((float)(x-(display_width/2))*hz_per_pixel)); } last_x=x; pressed=FALSE; @@ -199,7 +203,14 @@ void panadapter_update(float *data,int tx) { } else { hz_per_pixel=192000.0/(double)display_width; } - } + } /* else if(mode==modeFREEDV) { + saved_max=panadapter_high; + saved_min=panadapter_low; + saved_hz_per_pixel=hz_per_pixel; + panadapter_high=20; + panadapter_low=-100; + hz_per_pixel=48000.0/(double)display_width; + } */ //clear_panadater_surface(); cairo_t *cr; @@ -209,8 +220,8 @@ void panadapter_update(float *data,int tx) { // filter cairo_set_source_rgb (cr, 0.25, 0.25, 0.25); - filter_left=(double)display_width/2.0+((double)getFilterLow()/hz_per_pixel); - filter_right=(double)display_width/2.0+((double)getFilterHigh()/hz_per_pixel); + filter_left=(double)display_width/2.0+(((double)getFilterLow()+ddsOffset)/hz_per_pixel); + filter_right=(double)display_width/2.0+(((double)getFilterHigh()+ddsOffset)/hz_per_pixel); cairo_rectangle(cr, filter_left, 0.0, filter_right-filter_left, (double)panadapter_height); cairo_fill(cr); @@ -248,6 +259,7 @@ void panadapter_update(float *data,int tx) { divisor=5000L; break; case 96000: + case 100000: divisor=10000L; break; case 192000: @@ -259,7 +271,9 @@ void panadapter_update(float *data,int tx) { case 768000: divisor=50000L; break; + case 1048576: case 1536000: + case 2097152: divisor=100000L; break; } @@ -340,15 +354,30 @@ void panadapter_update(float *data,int tx) { cairo_set_line_width(cr, 1.0); cairo_stroke(cr); +#ifdef FREEDV + if(mode==modeFREEDV) { + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_rectangle(cr, (double)display_width/2.0+2.0, (double)panadapter_height-20.0, (double)display_width, (double)panadapter_height); + cairo_fill(cr); + cairo_set_source_rgb(cr, 0, 1, 0); + cairo_set_font_size(cr, 16); + cairo_move_to(cr,(double)display_width/2.0+5.0,(double)panadapter_height-2.0); + cairo_show_text(cr, freedv_rx_text_data); + } +#endif + cairo_destroy (cr); gtk_widget_queue_draw (panadapter); if(tx) { - panadapter_high=saved_max; - panadapter_low=saved_min; - hz_per_pixel=saved_hz_per_pixel; - } - + panadapter_high=saved_max; + panadapter_low=saved_min; + hz_per_pixel=saved_hz_per_pixel; + } /* else if(mode==modeFREEDV) { + panadapter_high=saved_max; + panadapter_low=saved_min; + hz_per_pixel=saved_hz_per_pixel; + } */ } //} } diff --git a/pihpsdr b/pihpsdr index 0d93d75eba890aca74d95d7fa8d238d60ee3c68a..5caddc8cd7d00fa637cef8c4b978c988b93851ec 100755 GIT binary patch literal 535492 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