]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
Ininital commit of source files
authorg0orx <john.d.melton@googlemail.com>
Wed, 3 Feb 2016 09:51:29 +0000 (20:21 +1030)
committerg0orx <john.d.melton@googlemail.com>
Wed, 3 Feb 2016 09:51:29 +0000 (20:21 +1030)
41 files changed:
Makefile [new file with mode: 0644]
agc.h [new file with mode: 0644]
alex.h [new file with mode: 0644]
band.c [new file with mode: 0644]
band.h [new file with mode: 0644]
bandstack.h [new file with mode: 0644]
calculus [new file with mode: 0644]
channel.h [new file with mode: 0644]
discovered.h [new file with mode: 0644]
filter.c [new file with mode: 0644]
filter.h [new file with mode: 0644]
main.c [new file with mode: 0644]
main.h [new file with mode: 0644]
meter.c [new file with mode: 0644]
meter.h [new file with mode: 0644]
mode.c [new file with mode: 0644]
mode.h [new file with mode: 0644]
new_discovery.c [new file with mode: 0644]
new_discovery.h [new file with mode: 0644]
new_protocol.c [new file with mode: 0644]
new_protocol.h [new file with mode: 0644]
new_protocol_programmer.c [new file with mode: 0644]
panadapter.c [new file with mode: 0644]
panadapter.h [new file with mode: 0644]
property.c [new file with mode: 0644]
property.h [new file with mode: 0644]
radio.c [new file with mode: 0644]
radio.h [new file with mode: 0644]
rotary_encoder.c [new file with mode: 0644]
rotary_encoder.h [new file with mode: 0644]
splash.c [new file with mode: 0644]
splash.h [new file with mode: 0644]
splash.png [new file with mode: 0644]
start_pihpsdr.sh [new file with mode: 0755]
toolbar.c [new file with mode: 0644]
toolbar.h [new file with mode: 0644]
vfo.c [new file with mode: 0644]
vfo.h [new file with mode: 0644]
waterfall.c [new file with mode: 0644]
waterfall.h [new file with mode: 0644]
xvtr.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..c5bae3b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,86 @@
+UNAME_N := $(shell uname -n)
+
+CC=gcc
+LINK=gcc
+OPTIONS=-g -D $(UNAME_N)
+GTKINCLUDES=`pkg-config --cflags gtk+-3.0`
+GTKLIBS=`pkg-config --libs gtk+-3.0`
+LIBS=-lwiringPi -lpigpio -lrt -lm -lwdsp -lpthread $(GTKLIBS)
+INCLUDES=$(GTKINCLUDES)
+
+COMPILE=$(CC) $(OPTIONS) $(INCLUDES)
+
+PROGRAM=pihpsdr
+
+
+SOURCES= \
+band.c \
+filter.c \
+main.c \
+meter.c \
+mode.c \
+new_discovery.c \
+new_protocol.c \
+new_protocol_programmer.c \
+panadapter.c \
+property.c \
+radio.c \
+rotary_encoder.c \
+splash.c \
+toolbar.c \
+vfo.c \
+waterfall.c
+
+HEADERS= \
+agc.h \
+alex.h \
+band.h \
+bandstack.h \
+channel.h \
+discovered.h \
+filter.h \
+meter.h \
+mode.h \
+new_discovery.h \
+new_protocol.h \
+panadapter.h \
+property.h \
+radio.h \
+rotary_encoder.h \
+splash.h \
+toolbar.h \
+vfo.h \
+waterfall.h \
+xvtr.h
+
+OBJS= \
+band.o \
+filter.o \
+main.o \
+meter.o \
+mode.o \
+new_discovery.o \
+new_protocol.o \
+new_protocol_programmer.o \
+panadapter.o \
+property.o \
+radio.o \
+rotary_encoder.o \
+splash.o \
+toolbar.o \
+vfo.o \
+waterfall.o
+
+all: $(PROGRAM) $(HEADERS) $(SOURCES)
+
+$(PROGRAM): $(OBJS)
+       $(LINK) -o $(PROGRAM) $(OBJS) $(LIBS)
+
+.c.o:
+       $(COMPILE) $(OPTIONS) -c -o $@ $<
+
+
+clean:
+       -rm -f *.o
+       -rm -f $(PROGRAM)
+
diff --git a/agc.h b/agc.h
new file mode 100644 (file)
index 0000000..c6e3aff
--- /dev/null
+++ b/agc.h
@@ -0,0 +1,6 @@
+#define AGC_OFF 0
+#define AGC_LONG 1
+#define AGC_SLOW 2
+#define AGC_MEDIUM 3
+#define AGC_FAST 4
+
diff --git a/alex.h b/alex.h
new file mode 100644 (file)
index 0000000..7ead806
--- /dev/null
+++ b/alex.h
@@ -0,0 +1,31 @@
+#define ALEX_RX_ANTENNA_NONE   0x00000000
+#define ALEX_RX_ANTENNA_XVTR   0x00000900
+#define ALEX_RX_ANTENNA_EXT1   0x00000A00
+#define ALEX_RX_ANTENNA_EXT2   0x00000C00
+#define ALEX_RX_ANTENNA_BYPASS 0x00000800
+
+#define ALEX_TX_ANTENNA_1      0x01000000
+#define ALEX_TX_ANTENNA_2      0x02000000
+#define ALEX_TX_ANTENNA_3      0x04000000
+
+#define ALEX_ATTENUATION_0dB   0x00000000
+#define ALEX_ATTENUATION_10dB  0x00004000
+#define ALEX_ATTENUATION_20dB  0x00002000
+#define ALEX_ATTENUATION_30dB  0x00006000
+
+#define ALEX_30_20_LPF         0x00100000
+#define ALEX_60_40_LPF         0x00200000
+#define ALEX_80_LPF            0x00400000
+#define ALEX_160_LPF           0x00800000
+#define ALEX_6_BYPASS_LPF      0x20000000
+#define ALEX_12_10_LPF         0x40000000
+#define ALEX_17_15_LPF         0x80000000
+
+#define ALEX_13MHZ_HPF         0x00000002
+#define ALEX_20MHZ_HPF         0x00000004
+#define ALEX_9_5MHZ_HPF        0x00000010
+#define ALEX_6_5MHZ_HPF        0x00000020
+#define ALEX_1_5MHZ_HPF        0x00000040
+#define ALEX_BYPASS_HPF        0x00000800
+
+#define ALEX_6M_PREAMP         0x00000008
diff --git a/band.c b/band.c
new file mode 100644 (file)
index 0000000..53ec328
--- /dev/null
+++ b/band.c
@@ -0,0 +1,344 @@
+#include <stdio.h>
+
+#include "bandstack.h"
+#include "band.h"
+#include "filter.h"
+#include "mode.h"
+#include "xvtr.h"
+#include "alex.h"
+#include "property.h"
+
+int band=band20;
+int xvtr_band=band160;
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief bandstack
+*/
+/* ----------------------------------------------------------------------------*/
+BANDSTACK_ENTRY bandstack_entries160[] =
+    {{1810000LL,modeCWL,filterF4,-2800,-200,-2800,-200},
+     {1835000LL,modeCWU,filterF0,-2800,-200,-2800,-200},
+     {1845000LL,modeUSB,filterF5,-2800,-200,-2800,-200}};
+
+BANDSTACK_ENTRY bandstack_entries80[] =
+    {{3501000LL,modeCWL,filterF0,-2800,-200,-2800,-200},
+     {3751000LL,modeLSB,filterF5,-2800,-200,-2800,-200},
+     {3850000LL,modeLSB,filterF5,-2800,-200,-2800,-200}};
+
+BANDSTACK_ENTRY bandstack_entries60[] =
+    {{5330500LL,modeUSB,filterF5,-2800,-200,-2800,-200},
+     {5346500LL,modeUSB,filterF5,-2800,-200,-2800,-200},
+     {5366500LL,modeUSB,filterF5,-2800,-200,-2800,-200},
+     {5371500LL,modeUSB,filterF5,-2800,-200,-2800,-200},
+     {5403500LL,modeUSB,filterF5,-2800,-200,-2800,-200}};
+
+BANDSTACK_ENTRY bandstack_entries40[] =
+    {{7001000LL,modeCWL,filterF0,-2800,-200,-2800,-200},
+     {7152000LL,modeLSB,filterF5,-2800,-200,-2800,-200},
+     {7255000LL,modeLSB,filterF5,-2800,-200,-2800,-200}};
+
+BANDSTACK_ENTRY bandstack_entries30[] =
+    {{10120000LL,modeCWU,filterF0,200,2800,200,2800},
+     {10130000LL,modeCWU,filterF0,200,2800,200,2800},
+     {10140000LL,modeCWU,filterF4,200,2800,200,2800}};
+
+BANDSTACK_ENTRY bandstack_entries20[] =
+    {{14010000LL,modeCWU,filterF0,200,2800,200,2800},
+     {14230000LL,modeUSB,filterF5,200,2800,200,2800},
+     {14336000LL,modeUSB,filterF5,200,2800,200,2800}};
+
+BANDSTACK_ENTRY bandstack_entries18[] =
+    {{18068600LL,modeCWU,filterF0,200,2800,200,2800},
+     {18125000LL,modeUSB,filterF5,200,2800,200,2800},
+     {18140000LL,modeUSB,filterF5,200,2800,200,2800}};
+
+BANDSTACK_ENTRY bandstack_entries15[] =
+    {{21001000LL,modeCWU,filterF0,200,2800,200,2800},
+     {21255000LL,modeUSB,filterF5,200,2800,200,2800},
+     {21300000LL,modeUSB,filterF5,200,2800,200,2800}};
+
+BANDSTACK_ENTRY bandstack_entries12[] =
+    {{24895000LL,modeCWU,filterF0,200,2800,200,2800},
+     {24900000LL,modeUSB,filterF5,200,2800,200,2800},
+     {24910000LL,modeUSB,filterF5,200,2800,200,2800}};
+
+BANDSTACK_ENTRY bandstack_entries10[] =
+    {{28010000LL,modeCWU,filterF0,200,2800,200,2800},
+     {28300000LL,modeUSB,filterF5,200,2800,200,2800},
+     {28400000LL,modeUSB,filterF5,200,2800,200,2800}};
+
+BANDSTACK_ENTRY bandstack_entries50[] =
+    {{50010000LL,modeCWU,filterF0,200,2800,200,2800},
+     {50125000LL,modeUSB,filterF5,200,2800,200,2800},
+     {50200000LL,modeUSB,filterF5,200,2800,200,2800}};
+
+BANDSTACK_ENTRY bandstack_entriesGEN[] =
+    {{909000LL,modeAM,filterF6,-6000,6000,-6000,60000},
+     {5975000LL,modeAM,filterF6,-6000,6000,-6000,60000},
+     {13845000LL,modeAM,filterF6,-6000,6000,-6000,60000}};
+
+BANDSTACK_ENTRY bandstack_entriesWWV[] =
+    {{2500000LL,modeSAM,filterF6,200,2800,200,2800},
+     {5000000LL,modeSAM,filterF6,200,2800,200,2800},
+     {10000000LL,modeSAM,filterF6,200,2800,200,2800},
+     {15000000LL,modeSAM,filterF6,200,2800,200,2800},
+     {20000000LL,modeSAM,filterF6,200,2800,200,2800}};
+
+BANDSTACK bandstack160={3,1,bandstack_entries160};
+BANDSTACK bandstack80={3,1,bandstack_entries80};
+BANDSTACK bandstack60={5,1,bandstack_entries60};
+BANDSTACK bandstack40={3,1,bandstack_entries40};
+BANDSTACK bandstack30={3,1,bandstack_entries30};
+BANDSTACK bandstack20={3,1,bandstack_entries20};
+BANDSTACK bandstack18={3,1,bandstack_entries18};
+BANDSTACK bandstack15={3,1,bandstack_entries15};
+BANDSTACK bandstack12={3,1,bandstack_entries12};
+BANDSTACK bandstack10={3,1,bandstack_entries10};
+BANDSTACK bandstack50={3,1,bandstack_entries50};
+BANDSTACK bandstackGEN={3,1,bandstack_entriesGEN};
+BANDSTACK bandstackWWV={5,1,bandstack_entriesWWV};
+
+BAND bands[BANDS] = 
+    {{"160",&bandstack160,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"80",&bandstack80,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"60",&bandstack60,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"40",&bandstack40,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"30",&bandstack30,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"20",&bandstack20,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"18",&bandstack18,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"15",&bandstack15,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"12",&bandstack12,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"10",&bandstack10,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"50",&bandstack50,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"GEN",&bandstackGEN,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB},
+     {"WWV",&bandstackWWV,0,0,0,ALEX_RX_ANTENNA_NONE,ALEX_TX_ANTENNA_1,ALEX_ATTENUATION_0dB}};
+
+#define NUM_BAND_LIMITS 22
+
+BAND_LIMITS bandLimits[NUM_BAND_LIMITS] = {
+    {1800000LL,2000000LL},
+    {3500000LL,4000000LL},
+    {5330500LL,5403500LL},
+    {7000000LL,7300000LL},
+    {10100000LL,10150000LL},
+    {14000000LL,14350000LL},
+    {18068000LL,18168000LL},
+    {21000000LL,21450000LL},
+    {24890000LL,24990000LL},
+    {28000000LL,29700000LL},
+    {50000000LL,54000000LL},
+    {144000000LL,148000000LL},
+    {222000000LL,224980000LL},
+    {420000000LL,450000000LL},
+    {902000000LL,928000000LL},
+    {1240000000LL,1300000000LL},
+    {2300000000LL,2450000000LL},
+    {3456000000LL,3456400000LL},
+    {5760000000LL,5760400000LL},
+    {10368000000LL,10368400000LL},
+    {24192000000LL,24192400000LL},
+    {47088000000LL,47088400000LL}
+};
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief xvtr
+*/
+/* ----------------------------------------------------------------------------*/
+static XVTR_ENTRY xvtr[12]=
+    {{"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0},
+     {"",0LL,0LL,0LL,0LL,0LL,0LL,0LL,0LL,0,modeUSB,filterF5,-2800,-200,-2800,-200,0,0,0,0}};
+
+
+BANDSTACK_ENTRY *bandstack_entry_get_current() {
+    BANDSTACK *bandstack=bands[band].bandstack;
+    BANDSTACK_ENTRY *entry=&bandstack->entry[bandstack->current_entry];
+    return entry;
+}
+
+BANDSTACK_ENTRY *bandstack_entry_next() {
+    BANDSTACK *bandstack=bands[band].bandstack;
+    bandstack->current_entry++;
+    if(bandstack->current_entry==bandstack->entries) {
+        bandstack->current_entry=0;
+    }
+    BANDSTACK_ENTRY *entry=&bandstack->entry[bandstack->current_entry];
+    return entry;
+}
+
+int band_get_current() {
+    return band;
+}
+
+BAND *band_get_current_band() {
+    BAND *b=&bands[band];
+    return b;
+}
+
+BAND *band_get_band(int b) {
+    BAND *band=&bands[b];
+    return band;
+}
+
+BAND *band_set_current(int b) {
+    band=b;
+    return &bands[b];
+}
+
+
+void bandSaveState() {
+    char name[128];
+    char value[128];
+    int current;
+    BANDSTACK_ENTRY* entry;
+
+    int b;
+    int stack;
+    for(b=0;b<BANDS;b++) {
+        sprintf(value,"%d",bands[b].bandstack->entries);
+        sprintf(name,"band.%d.entries",b);
+        setProperty(name,value);
+
+        sprintf(value,"%d",bands[b].bandstack->current_entry);
+        sprintf(name,"band.%d.current",b);
+        setProperty(name,value);
+
+        sprintf(value,"%d",bands[b].preamp);
+        sprintf(name,"band.%d.preamp",b);
+        setProperty(name,value);
+
+        sprintf(value,"%ld",bands[b].alexRxAntenna);
+        sprintf(name,"band.%d.alexRxAntenna",b);
+        setProperty(name,value);
+
+        sprintf(value,"%ld",bands[b].alexTxAntenna);
+        sprintf(name,"band.%d.alexTxAntenna",b);
+        setProperty(name,value);
+
+        sprintf(value,"%ld",bands[b].alexAttenuation);
+        sprintf(name,"band.%d.alexAttenuation",b);
+        setProperty(name,value);
+
+        for(stack=0;stack<bands[b].bandstack->entries;stack++) {
+            entry=bands[b].bandstack->entry;
+            entry+=stack;
+
+            sprintf(value,"%lld",entry->frequencyA);
+            sprintf(name,"band.%d.stack.%d.a",b,stack);
+            setProperty(name,value);
+
+            sprintf(value,"%d",entry->mode);
+            sprintf(name,"band.%d.stack.%d.mode",b,stack);
+            setProperty(name,value);
+
+            sprintf(value,"%d",entry->filter);
+            sprintf(name,"band.%d.stack.%d.filter",b,stack);
+            setProperty(name,value);
+
+            sprintf(value,"%d",entry->var1Low);
+            sprintf(name,"band.%d.stack.%d.var1Low",b,stack);
+            setProperty(name,value);
+
+            sprintf(value,"%d",entry->var1High);
+            sprintf(name,"band.%d.stack.%d.var1High",b,stack);
+            setProperty(name,value);
+
+            sprintf(value,"%d",entry->var2Low);
+            sprintf(name,"band.%d.stack.%d.var2Low",b,stack);
+            setProperty(name,value);
+
+            sprintf(value,"%d",entry->var2High);
+            sprintf(name,"band.%d.stack.%d.var2High",b,stack);
+            setProperty(name,value);
+
+        }
+    }
+
+    sprintf(value,"%d",band);
+    setProperty("band",value);
+}
+
+void bandRestoreState() {
+    char* value;
+
+    int b;
+    int stack;
+    char name[128];
+    BANDSTACK_ENTRY* entry;
+    int current;
+
+
+    for(b=0;b<BANDS;b++) {
+        sprintf(name,"band.%d.entries",b);
+        value=getProperty(name);
+        if(value) bands[b].bandstack->entries=atoi(value);
+
+        sprintf(name,"band.%d.current",b);
+        value=getProperty(name);
+        if(value) bands[b].bandstack->current_entry=atoi(value);
+
+        sprintf(name,"band.%d.preamp",b);
+        value=getProperty(name);
+        if(value) bands[b].preamp=atoi(value);
+
+        sprintf(name,"band.%d.alexRxAntenna",b);
+        value=getProperty(name);
+        if(value) bands[b].alexRxAntenna=atoi(value);
+
+        sprintf(name,"band.%d.alexTxAntenna",b);
+        value=getProperty(name);
+        if(value) bands[b].alexTxAntenna=atoi(value);
+
+        sprintf(name,"band.%d.alexAttenuation",b);
+        value=getProperty(name);
+        if(value) bands[b].alexAttenuation=atoi(value);
+        for(stack=0;stack<bands[b].bandstack->entries;stack++) {
+            entry=bands[b].bandstack->entry;
+            entry+=stack;
+
+            sprintf(name,"band.%d.stack.%d.a",b,stack);
+            value=getProperty(name);
+            if(value) entry->frequencyA=atoll(value);
+
+            sprintf(name,"band.%d.stack.%d.mode",b,stack);
+            value=getProperty(name);
+            if(value) entry->mode=atoi(value);
+
+            sprintf(name,"band.%d.stack.%d.filter",b,stack);
+            value=getProperty(name);
+            if(value) entry->filter=atoi(value);
+
+            sprintf(name,"band.%d.stack.%d.var1Low",b,stack);
+            value=getProperty(name);
+            if(value) entry->var1Low=atoi(value);
+
+            sprintf(name,"band.%d.stack.%d.var1High",b,stack);
+            value=getProperty(name);
+            if(value) entry->var1High=atoi(value);
+
+            sprintf(name,"band.%d.stack.%d.var2Low",b,stack);
+            value=getProperty(name);
+            if(value) entry->var2Low=atoi(value);
+
+            sprintf(name,"band.%d.stack.%d.var2High",b,stack);
+            value=getProperty(name);
+            if(value) entry->var2High=atoi(value);
+
+        }
+    }
+    value=getProperty("band");
+    if(value) band=atoi(value);
+}
+
+
diff --git a/band.h b/band.h
new file mode 100644 (file)
index 0000000..24d15c1
--- /dev/null
+++ b/band.h
@@ -0,0 +1,100 @@
+/**
+* @file band.h
+* @brief Header files for the Amateur Radio band stack.
+* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* @version 0.1
+* @date 2009-04-11
+*/
+// band.h
+
+/* Copyright (C)
+* 2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* 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 <gtk/gtk.h>
+#include "bandstack.h"
+
+#define band160 0
+#define band80 1
+#define band60 2
+#define band40 3
+#define band30 4
+#define band20 5
+#define band17 6
+#define band15 7
+#define band12 8
+#define band10 9
+#define band6 10
+#define bandGen 11
+#define bandWWV 12
+
+#define BANDS 13
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief Bandlimit definition
+*/
+struct _BAND_LIMITS {
+    long long minFrequency;
+    long long maxFrequency;
+};
+
+typedef struct _BAND_LIMITS BAND_LIMITS;
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief Band definition
+*/
+struct _BAND {
+    char title[16];
+    BANDSTACK *bandstack;
+    unsigned char OCrx;
+    unsigned char OCtx;
+    int preamp;
+    unsigned long alexRxAntenna;
+    unsigned long alexTxAntenna;
+    unsigned long alexAttenuation;
+};
+
+typedef struct _BAND BAND;
+
+int band;
+int xvtr_band;
+gboolean displayHF;
+
+int band_get_current();
+BAND *band_get_current_band();
+BAND *band_get_band(int b);
+BAND *band_set_current(int b);
+
+BANDSTACK_ENTRY *bandstack_entry_next();
+BANDSTACK_ENTRY *bandstack_entry_get_current();
+
+/*
+void bandSaveState();
+void bandRestoreState();
+void forceBand(int band,int setup);
+void configureXVTRButton(int setup);
+GtkWidget* buildBandUI();
+
+int remoteSetBand(gpointer *data);
+
+BAND_LIMITS* getBandLimits(long long minDisplay,long long maxDisplay);
+XVTR_ENTRY* getXvtrEntry(int i);
+
+*/
+
diff --git a/bandstack.h b/bandstack.h
new file mode 100644 (file)
index 0000000..2fb8e9a
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+* @file bandstack.h
+* @brief Bandstack definition files
+* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* @version 0.1
+* @date 2009-04-11
+*/
+// bandstack.h
+
+/* Copyright (C)
+* This program is free software; you can redistribute it and/or2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* 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.
+*
+*/
+
+#ifndef BANDSTACK_H
+#define BANDSTACK_H
+/* --------------------------------------------------------------------------*/
+/**
+* @brief Bandstack definition
+*/
+struct _BANDSTACK_ENTRY {
+    long long frequencyA;
+    int mode;
+    int filter;
+    int var1Low;
+    int var1High;
+    int var2Low;
+    int var2High;
+};
+
+typedef struct _BANDSTACK_ENTRY BANDSTACK_ENTRY;
+
+struct _BANDSTACK {
+    int entries;
+    int current_entry;
+    BANDSTACK_ENTRY *entry;
+};
+
+typedef struct _BANDSTACK BANDSTACK;
+
+#endif
+
diff --git a/calculus b/calculus
new file mode 100644 (file)
index 0000000..7b51e4f
Binary files /dev/null and b/calculus differ
diff --git a/channel.h b/channel.h
new file mode 100644 (file)
index 0000000..ae1e178
--- /dev/null
+++ b/channel.h
@@ -0,0 +1,12 @@
+#define CHANNEL_RX0 0
+#define CHANNEL_RX1 1
+#define CHANNEL_RX2 2
+#define CHANNEL_RX3 3
+#define CHANNEL_RX4 4
+#define CHANNEL_RX5 5
+#define CHANNEL_RX6 6
+#define CHANNEL_RX7 7
+#define CHANNEL_TX 8
+#define CHANNEL_BS 9
+#define CHANNEL_SUBRX 10
+
diff --git a/discovered.h b/discovered.h
new file mode 100644 (file)
index 0000000..e644e69
--- /dev/null
@@ -0,0 +1,45 @@
+#include <netinet/in.h>
+
+#define MAX_DEVICES 16
+
+#define DEVICE_METIS 0
+#define DEVICE_HERMES 1
+#define DEVICE_GRIFFIN 2
+#define DEVICE_ANGELIA 4
+#define DEVICE_ORION 5
+#define DEVICE_HERMES_LITE 6
+
+#define NEW_DEVICE_ATLAS 0
+#define NEW_DEVICE_HERMES 1
+#define NEW_DEVICE_HERMES2 2
+#define NEW_DEVICE_ANGELIA 3
+#define NEW_DEVICE_ORION 4
+#define NEW_DEVICE_ANAN_10E 5
+#define NEW_DEVICE_HERMES_LITE 6
+
+#define STATE_AVAILABLE 2
+#define STATE_SENDING 3
+
+#define ORIGINAL_PROTOCOL 0
+#define NEW_PROTOCOL 1
+
+struct _DISCOVERED {
+    int protocol;
+    int device;
+    char name[16];
+    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;
+    char interface_name[64];
+};
+
+typedef struct _DISCOVERED DISCOVERED;
+
+extern int selected_device;
+extern int devices;
+extern DISCOVERED discovered[MAX_DEVICES];
+
diff --git a/filter.c b/filter.c
new file mode 100644 (file)
index 0000000..e6b0fe5
--- /dev/null
+++ b/filter.c
@@ -0,0 +1,184 @@
+#include "filter.h"
+
+FILTER filterLSB[FILTERS]={
+    {-5150,-150,"5.0k"},
+    {-4550,-150,"4.4k"},
+    {-3950,-150,"3.8k"},
+    {-3450,-150,"3.3k"},
+    {-3050,-150,"2.9k"},
+    {-2850,-150,"2.7k"},
+    {-2550,-150,"2.4k"},
+    {-2250,-150,"2.1k"},
+    {-1950,-150,"1.8k"},
+    {-1150,-150,"1.0k"}/*,
+    {-2850,-150,"Var1"},
+    {-2850,-150,"Var2"}*/
+    };
+
+FILTER filterDIGL[FILTERS]={
+    {-5150,-150,"5.0k"},
+    {-4550,-150,"4.4k"},
+    {-3950,-150,"3.8k"},
+    {-3450,-150,"3.3k"},
+    {-3050,-150,"2.9k"},
+    {-2850,-150,"2.7k"},
+    {-2550,-150,"2.4k"},
+    {-2250,-150,"2.1k"},
+    {-1950,-150,"1.8k"},
+    {-1150,-150,"1.0k"}/*,
+    {-2850,-150,"Var1"},
+    {-2850,-150,"Var2"}*/
+    };
+
+FILTER filterUSB[FILTERS]={
+    {150,5150,"5.0k"},
+    {150,4550,"4.4k"},
+    {150,3950,"3.8k"},
+    {150,3450,"3.3k"},
+    {150,3050,"2.9k"},
+    {150,2850,"2.7k"},
+    {150,2550,"2.4k"},
+    {150,2250,"2.1k"},
+    {150,1950,"1.8k"},
+    {150,1150,"1.0k"}/*,
+    {150,2850,"Var1"},
+    {150,2850,"Var2"}*/
+    };
+
+FILTER filterDIGU[FILTERS]={
+    {150,5150,"5.0k"},
+    {150,4550,"4.4k"},
+    {150,3950,"3.8k"},
+    {150,3450,"3.3k"},
+    {150,3050,"2.9k"},
+    {150,2850,"2.7k"},
+    {150,2550,"2.4k"},
+    {150,2250,"2.1k"},
+    {150,1950,"1.8k"},
+    {150,1150,"1.0k"}/*,
+    {150,2850,"Var1"},
+    {150,2850,"Var2"}*/
+    };
+
+FILTER filterCWL[FILTERS]={
+    {500,500,"1.0k"},
+    {400,400,"800"},
+    {375,375,"750"},
+    {300,300,"600"},
+    {250,250,"500"},
+    {200,200,"400"},
+    {125,125,"250"},
+    {50,50,"100"},
+    {25,25,"50"},
+    {13,13,"25"}/*,
+    {250,250,"Var1"},
+    {250,250,"Var2"}*/
+    };
+
+FILTER filterCWU[FILTERS]={
+    {500,500,"1.0k"},
+    {400,400,"800"},
+    {375,375,"750"},
+    {300,300,"600"},
+    {250,250,"500"},
+    {200,200,"400"},
+    {125,125,"250"},
+    {50,50,"100"},
+    {25,25,"50"},
+    {13,13,"25"}/*,
+    {250,250,"Var1"},
+    {250,250,"Var2"}*/
+    };
+
+FILTER filterAM[FILTERS]={
+    {-8000,8000,"16k"},
+    {-6000,6000,"12k"},
+    {-5000,5000,"10k"},
+    {-4000,4000,"8k"},
+    {-3300,3300,"6.6k"},
+    {-2600,2600,"5.2k"},
+    {-2000,2000,"4.0k"},
+    {-1550,1550,"3.1k"},
+    {-1450,1450,"2.9k"},
+    {-1200,1200,"2.4k"}/*,
+    {-3300,3300,"Var1"},
+    {-3300,3300,"Var2"}*/
+    };
+
+FILTER filterSAM[FILTERS]={
+    {-8000,8000,"16k"},
+    {-6000,6000,"12k"},
+    {-5000,5000,"10k"},
+    {-4000,4000,"8k"},
+    {-3300,3300,"6.6k"},
+    {-2600,2600,"5.2k"},
+    {-2000,2000,"4.0k"},
+    {-1550,1550,"3.1k"},
+    {-1450,1450,"2.9k"},
+    {-1200,1200,"2.4k"}/*,
+    {-3300,3300,"Var1"},
+    {-3300,3300,"Var2"}*/
+    };
+
+FILTER filterFMN[FILTERS]={
+    {-8000,8000,"16k"},
+    {-6000,6000,"12k"},
+    {-5000,5000,"10k"},
+    {-4000,4000,"8k"},
+    {-3300,3300,"6.6k"},
+    {-2600,2600,"5.2k"},
+    {-2000,2000,"4.0k"},
+    {-1550,1550,"3.1k"},
+    {-1450,1450,"2.9k"},
+    {-1200,1200,"2.4k"}/*,
+    {-3300,3300,"Var1"},
+    {-3300,3300,"Var2"}*/
+    };
+
+FILTER filterDSB[FILTERS]={
+    {-8000,8000,"16k"},
+    {-6000,6000,"12k"},
+    {-5000,5000,"10k"},
+    {-4000,4000,"8k"},
+    {-3300,3300,"6.6k"},
+    {-2600,2600,"5.2k"},
+    {-2000,2000,"4.0k"},
+    {-1550,1550,"3.1k"},
+    {-1450,1450,"2.9k"},
+    {-1200,1200,"2.4k"}/*,
+    {-3300,3300,"Var1"},
+    {-3300,3300,"Var2"}*/
+    };
+
+FILTER filterSPEC[FILTERS]={
+    {-8000,8000,"16k"},
+    {-6000,6000,"12k"},
+    {-5000,5000,"10k"},
+    {-4000,4000,"8k"},
+    {-3300,3300,"6.6k"},
+    {-2600,2600,"5.2k"},
+    {-2000,2000,"4.0k"},
+    {-1550,1550,"3.1k"},
+    {-1450,1450,"2.9k"},
+    {-1200,1200,"2.4k"}/*,
+    {-3300,3300,"Var1"},
+    {-3300,3300,"Var2"}*/
+    };
+
+FILTER filterDRM[FILTERS]={
+    {-8000,8000,"16k"},
+    {-6000,6000,"12k"},
+    {-5000,5000,"10k"},
+    {-4000,4000,"8k"},
+    {-3300,3300,"6.6k"},
+    {-2600,2600,"5.2k"},
+    {-2000,2000,"4.0k"},
+    {-1550,1550,"3.1k"},
+    {-1450,1450,"2.9k"},
+    {-1200,1200,"2.4k"}/*,
+    {-3300,3300,"Var1"},
+    {-3300,3300,"Var2"}*/
+    };
+
+FILTER *filters[]={filterLSB,filterUSB,filterDSB,filterCWL,filterCWU,filterFMN,filterAM,filterDIGU,filterSPEC,filterDIGL,filterSAM,filterDRM};
+
diff --git a/filter.h b/filter.h
new file mode 100644 (file)
index 0000000..ad72c9f
--- /dev/null
+++ b/filter.h
@@ -0,0 +1,79 @@
+/**
+* @file filter.h
+* @brief Header files to define the filters.
+* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* @version 0.1
+* @date 2009-04-11
+*/
+// filter.h
+/* Copyright (C)
+* 2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* 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 "mode.h"
+
+// disable Var1 and Var2 (change to 12 to enable)
+#define FILTERS 10
+
+#define CW_PITCH 600
+
+struct _FILTER {
+    int low;
+    int high;
+    char* title;
+};
+
+typedef struct _FILTER FILTER;
+
+#define filterF0 0
+#define filterF1 1
+#define filterF2 2
+#define filterF3 3
+#define filterF4 4
+#define filterF5 5
+#define filterF6 6
+#define filterF7 7
+#define filterF8 8
+#define filterF9 9
+#define filterVar1 10
+#define filterVar2 11
+
+int filter;
+
+int filterLow;
+int filterHigh;
+
+int txFilterLowCut;
+int txFilterHighCut;
+
+int filterVar1Low;
+int filterVar1High;
+int filterVar2Low;
+int filterVar2High;
+
+
+FILTER *filters[MODES];
+
+/*
+int updateFilter(void * data);
+GtkWidget* buildFilterUI();
+void filterSaveState();
+void filterRestoreState();
+void setFilterValues(int mode);
+void setFilter(int filter);
+void setTxFilters();
+*/
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..814e9f5
--- /dev/null
+++ b/main.c
@@ -0,0 +1,345 @@
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <math.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "main.h"
+#include "channel.h"
+#include "discovered.h"
+#include "new_discovery.h"
+#include "new_protocol.h"
+#include "wdsp.h"
+#include "vfo.h"
+#include "meter.h"
+#include "panadapter.h"
+#include "splash.h"
+#include "waterfall.h"
+#include "toolbar.h"
+#include "radio.h"
+
+#define VFO_HEIGHT (display_height/8)
+#define VFO_WIDTH ((display_width/4)*3)
+#define METER_HEIGHT (display_height/8)
+#define METER_WIDTH (display_width/4)
+#define PANADAPTER_HEIGHT (display_height/4)
+#define TOOLBAR_HEIGHT (display_height/4)
+#define WATERFALL_HEIGHT (display_height-(VFO_HEIGHT+PANADAPTER_HEIGHT+TOOLBAR_HEIGHT))
+
+struct utsname unameData;
+
+gint display_width;
+gint display_height;
+
+static gint update_timer_id;
+static gint updates_per_second=10;
+
+static gint save_timer_id;
+
+static float *samples;
+
+static int start=0;
+
+static GtkWidget *discovery_dialog;
+
+gint update(gpointer data) {
+    int result;
+    GetPixels(isTransmitting()==0?CHANNEL_RX0:CHANNEL_TX,samples,&result);
+    if(result==1) {
+        panadapter_update(samples,isTransmitting());
+        if(!isTransmitting()) {
+            waterfall_update(samples);
+        }
+    }
+
+    if(!isTransmitting()) {
+        float m=GetRXAMeter(CHANNEL_RX0, 1/*WDSP.S_AV*/);
+        meter_update(SMETER,(double)m,0.0);
+    } else {
+
+        DISCOVERED *d=&discovered[selected_device];
+
+        double constant1=5.0;
+        double constant2=0.108;
+
+        switch(d->device) {
+            case NEW_DEVICE_ATLAS:
+                constant1=3.3;
+                constant2=0.09;
+                break;
+            case NEW_DEVICE_HERMES:
+                constant1=3.3;
+                constant2=0.09;
+                break;
+            case NEW_DEVICE_HERMES2:
+                constant1=3.3;
+                constant2=0.095;
+                break;
+            case NEW_DEVICE_ANGELIA:
+                constant1=3.3;
+                constant2=0.095;
+                break;
+            case NEW_DEVICE_ORION:
+                constant1=5.0;
+                constant2=0.108;
+                break;
+            case NEW_DEVICE_ANAN_10E:
+                constant1=3.3;
+                constant2=0.09;
+                break;
+            case NEW_DEVICE_HERMES_LITE:
+                constant1=3.3;
+                constant2=0.09;
+                break;
+        }
+        
+        int power=alex_forward_power;
+        if(power==0) {
+            power=exciter_power;
+        }
+        double v1;
+        double fwd;
+        double rev;
+        v1=((double)power/4095.0)*constant1;
+        fwd=(v1*v1)/constant2;
+
+        rev=0.0;
+        if(alex_forward_power!=0) {
+            power=alex_reverse_power;
+            v1=((double)power/4095.0)*constant1;
+            rev=(v1*v1)/constant2;
+        }
+        meter_update(POWER,fwd,rev);
+    }
+
+    return TRUE;
+}
+
+static gint save_cb(gpointer data) {
+    radioSaveState();
+    saveProperties(property_path);
+    return TRUE;
+}
+
+static void start_cb(GtkWidget *widget, gpointer data) {
+fprintf(stderr,"start_cb\n");
+    selected_device=(int)data;
+    start=1;
+    gtk_widget_destroy(discovery_dialog);
+}
+
+static void configure_cb(GtkWidget *widget, gpointer data) {
+}
+
+
+gint init(void* arg) {
+
+  GtkWidget *window;
+  GtkWidget *grid;
+  GtkWidget *vfo;
+  GtkWidget *meter;
+  GtkWidget *panadapter;
+  GtkWidget *waterfall;
+  GtkWidget *toolbar;
+
+  DISCOVERED* d;
+
+  char wisdom_directory[1024];
+  char wisdom_file[1024];
+
+  fprintf(stderr,"init\n");
+
+  // check if wisdom file exists
+  getcwd(wisdom_directory, sizeof(wisdom_directory));
+  strcpy(&wisdom_directory[strlen(wisdom_directory)],"/");
+  strcpy(wisdom_file,wisdom_directory);
+  strcpy(&wisdom_file[strlen(wisdom_file)],"wdspWisdom");
+  if(access(wisdom_file,F_OK)<0) {
+      WDSPwisdom (wisdom_directory);
+  }
+
+  while(!start) {
+      new_discovery();
+      if(devices==0) {
+          fprintf(stderr,"No devices found!\n");
+          GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
+          discovery_dialog = gtk_message_dialog_new (GTK_WINDOW(splash_window),
+                                 flags,
+                                 GTK_MESSAGE_ERROR,
+                                 GTK_BUTTONS_OK_CANCEL,
+                                 "No devices found! Retry Discovery?");
+          gint result=gtk_dialog_run (GTK_DIALOG (discovery_dialog));
+          if(result==GTK_RESPONSE_CANCEL) {
+               _exit(0);
+          }
+      } else if(devices==1) {
+          selected_device=0;
+          start=1;
+      } else {
+          fprintf(stderr,"%s: found %d devices.\n", (char *)arg, devices);
+          GtkDialogFlags flags=GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
+          discovery_dialog = gtk_dialog_new_with_buttons ("Discovered",
+                                      GTK_WINDOW(splash_window),
+                                      flags,
+                                      "Discover",
+                                      GTK_RESPONSE_REJECT,
+                                      NULL);
+
+          GtkWidget *content;
+
+//          g_signal_connect_swapped(discovery_dialog,"response",G_CALLBACK(gtk_widget_destroy),discovery_dialog);
+
+          content=gtk_dialog_get_content_area(GTK_DIALOG(discovery_dialog));
+
+          GtkWidget *grid=gtk_grid_new();
+          gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+          gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+
+          int i;
+          char text[128];
+          for(i=0;i<devices;i++) {
+              d=&discovered[i];
+              sprintf(text,"%s %s (%02X:%02X:%02X:%02X:%02X:%02X) on interface %s\n",
+                        d->name,
+                        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);
+
+              GtkWidget *label=gtk_label_new(text);
+              gtk_widget_show(label);
+              gtk_grid_attach(GTK_GRID(grid),label,0,i,3,1);
+
+              GtkWidget *start_button=gtk_button_new_with_label("Start");
+              gtk_widget_show(start_button);
+              gtk_grid_attach(GTK_GRID(grid),start_button,3,i,1,1);
+              g_signal_connect(start_button,"pressed",G_CALLBACK(start_cb),(gpointer *)i);
+
+              GtkWidget *configure_button=gtk_button_new_with_label("Configure");
+              gtk_widget_show(configure_button);
+              gtk_grid_attach(GTK_GRID(grid),configure_button,4,i,1,1);
+              g_signal_connect(configure_button,"pressed",G_CALLBACK(configure_cb),(gpointer *)i);
+          }
+
+          gtk_container_add (GTK_CONTAINER (content), grid);
+          gtk_widget_show_all(discovery_dialog);
+          gint result=gtk_dialog_run(GTK_DIALOG(discovery_dialog));
+      }
+  }
+
+  d=&discovered[selected_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]);
+
+  loadProperties(property_path);
+  radioRestoreState();
+
+  samples=malloc(display_width*sizeof(float));
+
+  //selected_device=0;
+  setSampleRate(48000);
+  new_protocol_init(0,display_width);
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title (GTK_WINDOW (window), "pihpsdr");
+
+  //g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
+
+  gtk_container_set_border_width (GTK_CONTAINER (window), 0);
+
+  grid = gtk_grid_new();
+  gtk_container_add(GTK_CONTAINER(window), grid);
+
+  vfo = vfo_init(VFO_WIDTH,VFO_HEIGHT,window);
+  gtk_grid_attach(GTK_GRID(grid), vfo, 0, 0, 3, 1);
+
+  meter = meter_init(METER_WIDTH,METER_HEIGHT);
+  gtk_grid_attach(GTK_GRID(grid), meter, 3, 0, 1, 1);
+
+  panadapter = panadapter_init(display_width,PANADAPTER_HEIGHT);
+  gtk_grid_attach(GTK_GRID(grid), panadapter, 0, 1, 4, 1);
+
+  waterfall = waterfall_init(display_width,WATERFALL_HEIGHT);
+  gtk_grid_attach(GTK_GRID(grid), waterfall, 0, 2, 4, 1);
+
+  toolbar = toolbar_init(display_width,TOOLBAR_HEIGHT,window);
+  gtk_grid_attach(GTK_GRID(grid), toolbar, 0, 3, 4, 1);
+
+  splash_close();
+
+
+  gtk_widget_show_all (window);
+
+  gtk_window_fullscreen(GTK_WINDOW(window));
+
+  GdkCursor *cursor=gdk_cursor_new(GDK_ARROW);
+  GdkWindow *gdk_window = gtk_widget_get_window(window);
+  gdk_window_set_cursor(gdk_window,cursor);
+
+/*
+  GdkDisplay *display=gdk_display_get_default();
+  if(display==NULL) {
+    fprintf(stderr,"no default display!\n");
+    _exit(0);
+  }
+
+  GdkCursor *cursor=gdk_cursor_new_for_display(display,GDK_ARROW);
+*/
+
+  update_timer_id=gdk_threads_add_timeout(1000/updates_per_second, update, NULL);
+
+  // save every 30 seconds
+  save_timer_id=gdk_threads_add_timeout(30000, save_cb, NULL);
+
+  vfo_update(NULL);
+
+  return 0;
+}
+
+
+int
+main (int   argc,
+      char *argv[])
+{
+  gtk_init (&argc, &argv);
+
+  uname(&unameData);
+  fprintf(stderr,"sysname: %s\n",unameData.sysname);
+  fprintf(stderr,"nodename: %s\n",unameData.nodename);
+  fprintf(stderr,"release: %s\n",unameData.release);
+  fprintf(stderr,"version: %s\n",unameData.version);
+  fprintf(stderr,"machine: %s\n",unameData.machine);
+
+  GdkScreen *screen=gdk_screen_get_default();
+  if(screen==NULL) {
+    fprintf(stderr,"no default screen!\n");
+    _exit(0);
+  }
+
+
+  //display_width=gdk_screen_get_width(screen);
+  //display_height=gdk_screen_get_height(screen);
+  display_width=800;
+  display_height=480;
+
+  splash_show("splash.png", 0, display_width, display_height);
+
+  g_idle_add(init,(void *)argv[0]);
+
+  gtk_main();
+
+  return 0;
+}
diff --git a/main.h b/main.h
new file mode 100644 (file)
index 0000000..1e1d955
--- /dev/null
+++ b/main.h
@@ -0,0 +1,2 @@
+#include <sys/utsname.h>
+extern struct utsname unameData;
diff --git a/meter.c b/meter.c
new file mode 100644 (file)
index 0000000..2d77107
--- /dev/null
+++ b/meter.c
@@ -0,0 +1,182 @@
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "meter.h"
+
+static GtkWidget *meter;
+static cairo_surface_t *meter_surface = NULL;
+
+static int meter_width;
+static int meter_height;
+
+static void
+meter_clear_surface (void)
+{
+  cairo_t *cr;
+  cr = cairo_create (meter_surface);
+
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  cairo_paint (cr);
+
+  cairo_destroy (cr);
+}
+
+meter_configure_event_cb (GtkWidget         *widget,
+            GdkEventConfigure *event,
+            gpointer           data)
+{
+  if (meter_surface)
+    cairo_surface_destroy (meter_surface);
+
+  meter_surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
+                                       CAIRO_CONTENT_COLOR,
+                                       gtk_widget_get_allocated_width (widget),
+                                       gtk_widget_get_allocated_height (widget));
+
+  /* Initialize the surface to black */
+  meter_clear_surface ();
+
+  return TRUE;
+}
+
+/* Redraw the screen from the surface. Note that the ::draw
+ * signal receives a ready-to-be-used cairo_t that is already
+ * clipped to only draw the exposed areas of the widget
+ */
+static gboolean
+meter_draw_cb (GtkWidget *widget, cairo_t   *cr, gpointer   data) {
+  cairo_set_source_surface (cr, meter_surface, 0, 0);
+  cairo_paint (cr);
+
+  return FALSE;
+}
+
+GtkWidget* meter_init(int width,int height) {
+
+  meter_width=width;
+  meter_height=height;
+
+  meter = gtk_drawing_area_new ();
+  gtk_widget_set_size_request (meter, width, height);
+  /* Signals used to handle the backing surface */
+  g_signal_connect (meter, "draw",
+            G_CALLBACK (meter_draw_cb), NULL);
+  g_signal_connect (meter,"configure-event",
+            G_CALLBACK (meter_configure_event_cb), NULL);
+
+
+  return meter;
+}
+
+
+void meter_update(int meter_type,double value,double reverse) {
+  
+  char sf[32];
+  int text_location;
+  cairo_t *cr;
+  cr = cairo_create (meter_surface);
+
+  // clear the meter
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  cairo_paint (cr);
+
+  cairo_set_source_rgb(cr, 1, 1, 1);
+  switch(meter_type) {
+    case SMETER:
+      // value is dBm
+      text_location=10;
+      cairo_select_font_face(cr, "Arial",
+                    CAIRO_FONT_SLANT_NORMAL,
+                    CAIRO_FONT_WEIGHT_BOLD);
+      double level=value+(double)get_attenuation();
+      if(meter_width>=114) {
+        int db=meter_width/114; // S9+60 (9*6)+60
+        int i;
+        cairo_set_line_width(cr, 1.0);
+        cairo_set_source_rgb(cr, 1, 1, 1);
+        for(i=0;i<54;i++) {
+          cairo_move_to(cr,(double)(i*db),(double)meter_height-20);
+          if(i%18==0) {
+            cairo_line_to(cr,(double)(i*db),(double)(meter_height-30));
+          } else if(i%6==0) {
+            cairo_line_to(cr,(double)(i*db),(double)(meter_height-25));
+          }
+        }
+        cairo_stroke(cr);
+
+        cairo_set_font_size(cr, 12);
+        cairo_move_to(cr, (double)(18*db)-3.0, (double)meter_height);
+        cairo_show_text(cr, "3");
+        cairo_move_to(cr, (double)(36*db)-3.0, (double)meter_height);
+        cairo_show_text(cr, "6");
+
+        cairo_set_source_rgb(cr, 1, 0, 0);
+        cairo_move_to(cr,(double)(54*db),(double)meter_height-20);
+        cairo_line_to(cr,(double)(54*db),(double)(meter_height-30));
+        cairo_move_to(cr,(double)(74*db),(double)meter_height-20);
+        cairo_line_to(cr,(double)(74*db),(double)(meter_height-30));
+        cairo_move_to(cr,(double)(94*db),(double)meter_height-20);
+        cairo_line_to(cr,(double)(94*db),(double)(meter_height-30));
+        cairo_move_to(cr,(double)(114*db),(double)meter_height-20);
+        cairo_line_to(cr,(double)(114*db),(double)(meter_height-30));
+        cairo_stroke(cr);
+
+        cairo_move_to(cr, (double)(54*db)-3.0, (double)meter_height);
+        cairo_show_text(cr, "9");
+        cairo_move_to(cr, (double)(74*db)-12.0, (double)meter_height);
+        cairo_show_text(cr, "+20");
+        cairo_move_to(cr, (double)(94*db)-9.0, (double)meter_height);
+        cairo_show_text(cr, "+40");
+        cairo_move_to(cr, (double)(114*db)-6.0, (double)meter_height);
+        cairo_show_text(cr, "+60");
+
+        cairo_set_source_rgb(cr, 0, 1, 0);
+        cairo_move_to(cr,(double)((level+127.0)*db),(double)meter_height-30);
+        cairo_line_to(cr,(double)((level+127.0)*db),(double)(meter_height-50));
+        cairo_stroke(cr);
+
+        text_location=(db*114)+5;
+      }
+
+      cairo_set_font_size(cr, 16);
+      sprintf(sf,"%d dBm",(int)level);
+      cairo_move_to(cr, text_location, 45);
+      cairo_show_text(cr, sf);
+      break;
+    case POWER:
+      // value is Watts
+      cairo_select_font_face(cr, "Arial",
+            CAIRO_FONT_SLANT_NORMAL,
+            CAIRO_FONT_WEIGHT_BOLD);
+      cairo_set_font_size(cr, 18);
+
+      sprintf(sf,"FWD: %3.2f W",value);
+      cairo_move_to(cr, 10, 25);
+      cairo_show_text(cr, sf);
+
+      // value is Watts
+      double swr=(value+reverse)/(value-reverse);
+      cairo_select_font_face(cr, "Arial",
+            CAIRO_FONT_SLANT_NORMAL,
+            CAIRO_FONT_WEIGHT_BOLD);
+      cairo_set_font_size(cr, 18);
+
+      sprintf(sf,"SWR: %1.2f:1",swr);
+      cairo_move_to(cr, 10, 45);
+      cairo_show_text(cr, sf);
+      
+/*
+      sprintf(sf,"REV: %3.2f W",reverse);
+      cairo_move_to(cr, 10, 45);
+      cairo_show_text(cr, sf);
+*/
+      break;
+  }
+
+  cairo_destroy(cr);
+  gtk_widget_queue_draw (meter);
+}
diff --git a/meter.h b/meter.h
new file mode 100644 (file)
index 0000000..ad197d3
--- /dev/null
+++ b/meter.h
@@ -0,0 +1,6 @@
+#define SMETER 0
+#define POWER 1
+
+
+GtkWidget* meter_init(int width,int height);
+void meter_update(int meter_type,double value,double reverse);
diff --git a/mode.c b/mode.c
new file mode 100644 (file)
index 0000000..98fdfa5
--- /dev/null
+++ b/mode.c
@@ -0,0 +1,2 @@
+char *mode_string[]={"LSB","USB","DSB","CWL","CWU","FMN","AM","DIGU","SPEC","DIGL","SAM","DRM"};
+
diff --git a/mode.h b/mode.h
new file mode 100644 (file)
index 0000000..a4766a0
--- /dev/null
+++ b/mode.h
@@ -0,0 +1,55 @@
+/**
+* @file mode.h
+* @brief Header files for the mode functions
+* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* @version 0.1
+* @date 2009-04-12
+*/
+// mode.h
+
+/* Copyright (C)
+* 2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* 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.
+*
+*/
+
+#define modeLSB 0
+#define modeUSB 1
+#define modeDSB 2
+#define modeCWL 3
+#define modeCWU 4
+#define modeFMN 5
+#define modeAM 6
+#define modeDIGU 7
+#define modeSPEC 8
+#define modeDIGL 9
+#define modeSAM 10
+#define modeDRM 11
+
+#define MODES 12
+
+int mode;
+
+char *mode_string[MODES];
+
+/*
+int updateMode(void * data);
+
+void setMode(int mode);
+void modeSaveState();
+void modeRestoreState();
+GtkWidget* buildModeUI();
+char* modeToString();
+*/
diff --git a/new_discovery.c b/new_discovery.c
new file mode 100644 (file)
index 0000000..fe4a05a
--- /dev/null
@@ -0,0 +1,250 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <ifaddrs.h>
+#include <pthread.h>
+#include <string.h>
+#include <errno.h>
+
+#include "discovered.h"
+//#include "discovery.h"
+
+int selected_device=0;
+int devices=0;
+DISCOVERED discovered[MAX_DEVICES];
+
+static char interface_name[64];
+static struct sockaddr_in interface_addr={0};
+static int interface_length;
+
+#define DISCOVERY_PORT 1024
+static int discovery_socket;
+static struct sockaddr_in discovery_addr;
+
+void new_discover(struct ifaddrs* iface);
+
+static pthread_t discover_thread_id;
+void* new_discover_receive_thread(void* arg);
+
+void print_device(int i) {
+    fprintf(stderr,"discovery: found protocol=%d device=%d software_version=%d status=%d address=%s (%02X:%02X:%02X:%02X:%02X:%02X) on %s\n", 
+        discovered[i].protocol,
+        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);
+}
+
+void new_discovery() {
+    devices=0;
+    selected_device=0;
+    struct ifaddrs *addrs,*ifa;
+    getifaddrs(&addrs);
+    ifa = addrs;
+    while (ifa) {
+        if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
+            if((ifa->ifa_flags&IFF_UP)==IFF_UP
+                && (ifa->ifa_flags&IFF_RUNNING)==IFF_RUNNING
+                && (ifa->ifa_flags&IFF_LOOPBACK)!=IFF_LOOPBACK) {
+                new_discover(ifa);
+            }
+        }
+        ifa = ifa->ifa_next;
+    }
+    freeifaddrs(addrs);
+
+    
+    fprintf(stderr, "new_discovery found %d devices\n",devices);
+    
+    int i;
+    for(i=0;i<devices;i++) {
+        print_device(i);
+    }
+}
+
+void new_discover(struct ifaddrs* iface) {
+    int rc;
+    struct sockaddr_in *sa;
+    //char *addr;
+
+    strcpy(interface_name,iface->ifa_name);
+    fprintf(stderr,"new_discover: looking for HPSDR devices on %s\n",interface_name);
+
+    // send a broadcast to locate metis boards on the network
+    discovery_socket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
+    if(discovery_socket<0) {
+        perror("new_discover: create socket failed for discovery_socket\n");
+        exit(-1);
+    }
+
+    int optval = 1;
+    setsockopt(discovery_socket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
+
+    sa = (struct sockaddr_in *) iface->ifa_addr;
+    //addr = inet_ntoa(sa->sin_addr);
+
+    // 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);
+    if(bind(discovery_socket,(struct sockaddr*)&interface_addr,sizeof(interface_addr))<0) {
+        perror("new_discover: bind socket failed for discovery_socket\n");
+        exit(-1);
+    }
+
+    fprintf(stderr,"new_discover: bound to %s\n",interface_name);
+
+    // allow broadcast on the socket
+    int on=1;
+    rc=setsockopt(discovery_socket, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
+    if(rc != 0) {
+        fprintf(stderr,"new_discover: cannot set SO_BROADCAST: rc=%d\n", rc);
+        exit(-1);
+    }
+
+    // setup to address
+    struct sockaddr_in to_addr={0};
+    to_addr.sin_family=AF_INET;
+    to_addr.sin_port=htons(DISCOVERY_PORT);
+    to_addr.sin_addr.s_addr=htonl(INADDR_BROADCAST);
+
+    // start a receive thread to collect discovery response packets
+    rc=pthread_create(&discover_thread_id,NULL,new_discover_receive_thread,NULL);
+    if(rc != 0) {
+        fprintf(stderr,"pthread_create failed on new_discover_receive_thread: rc=%d\n", rc);
+        exit(-1);
+    }
+
+
+    // send discovery packet
+    unsigned char buffer[60];
+    buffer[0]=0x00;
+    buffer[1]=0x00;
+    buffer[2]=0x00;
+    buffer[3]=0x00;
+    buffer[4]=0x02;
+    int i;
+    for(i=5;i<60;i++) {
+        buffer[i]=0x00;
+    }
+
+    if(sendto(discovery_socket,buffer,60,0,(struct sockaddr*)&to_addr,sizeof(to_addr))<0) {
+        perror("new_discover: sendto socket failed for discovery_socket\n");
+        exit(-1);
+    }
+
+    // wait for receive thread to complete
+    void* status;
+    pthread_join(discover_thread_id,&status);
+
+    close(discovery_socket);
+
+    fprintf(stderr,"new_discover: exiting discover for %s\n",iface->ifa_name);
+}
+
+void* new_discover_receive_thread(void* arg) {
+    struct sockaddr_in addr;
+    int len;
+    unsigned char buffer[2048];
+    int bytes_read;
+    struct timeval tv;
+    int i;
+
+    tv.tv_sec = 1;
+    tv.tv_usec = 0;
+
+    setsockopt(discovery_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
+
+    len=sizeof(addr);
+    while(1) {
+        bytes_read=recvfrom(discovery_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&addr,&len);
+        if(bytes_read<0) {
+            fprintf(stderr,"new_discover: bytes read %d\n", bytes_read);
+            perror("new_discover: recvfrom socket failed for discover_receive_thread");
+            break;
+        }
+        fprintf(stderr,"new_discover: received %d bytes\n",bytes_read);
+        if(bytes_read==1444) {
+            if(devices>0) {
+                break;
+            }
+        } else {
+        if(buffer[0]==0 && buffer[1]==0 && buffer[2]==0 && buffer[3]==0) {
+            int status = buffer[4] & 0xFF;
+            if (status == 2 || status == 3) {
+                if(devices<MAX_DEVICES) {
+                    discovered[devices].protocol=NEW_PROTOCOL;
+                    discovered[devices].device=buffer[11]&0xFF;
+                    switch(discovered[devices].device) {
+                       case NEW_DEVICE_ATLAS:
+                            strcpy(discovered[devices].name,"Atlas");
+                            break;
+                       case NEW_DEVICE_HERMES:
+                            strcpy(discovered[devices].name,"Hermes");
+                            break;
+                       case NEW_DEVICE_HERMES2:
+                            strcpy(discovered[devices].name,"Hermes2");
+                            break;
+                       case NEW_DEVICE_ANGELIA:
+                            strcpy(discovered[devices].name,"Angelia");
+                            break;
+                       case NEW_DEVICE_ORION:
+                            strcpy(discovered[devices].name,"Orion");
+                            break;
+                       case NEW_DEVICE_ANAN_10E:
+                            strcpy(discovered[devices].name,"Anan-10E");
+                            break;
+                       case NEW_DEVICE_HERMES_LITE:
+                            strcpy(discovered[devices].name,"Hermes Lite");
+                            break;
+                        default:
+                            strcpy(discovered[devices].name,"Unknown");
+                            break;
+                    }
+                    discovered[devices].software_version=buffer[13]&0xFF;
+                    for(i=0;i<6;i++) {
+                        discovered[devices].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));
+                    discovered[devices].interface_length=sizeof(interface_addr);
+                    strcpy(discovered[devices].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);
+                    devices++;
+                }
+            }
+        }
+        }
+    }
+    fprintf(stderr,"new_discover: exiting new_discover_receive_thread\n");
+    pthread_exit(NULL);
+}
diff --git a/new_discovery.h b/new_discovery.h
new file mode 100644 (file)
index 0000000..fefa81a
--- /dev/null
@@ -0,0 +1,2 @@
+
+void new_discovery(void);
diff --git a/new_protocol.c b/new_protocol.c
new file mode 100644 (file)
index 0000000..a3b9687
--- /dev/null
@@ -0,0 +1,1066 @@
+
+//#define ECHO_MIC
+
+#include <gtk/gtk.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <math.h>
+
+#include "alex.h"
+#include "new_protocol.h"
+#include "channel.h"
+#include "discovered.h"
+#include "wdsp.h"
+#include "radio.h"
+#include "vfo.h"
+#include "toolbar.h"
+
+#define PI 3.1415926535897932F
+
+static int receiver;
+static int running=0;
+
+int data_socket;
+
+static struct sockaddr_in base_addr;
+static int base_addr_length;
+
+static struct sockaddr_in receiver_addr;
+static int receiver_addr_length;
+
+static struct sockaddr_in transmitter_addr;
+static int transmitter_addr_length;
+
+static struct sockaddr_in high_priority_addr;
+static int high_priority_addr_length;
+
+static struct sockaddr_in audio_addr;
+static int audio_addr_length;
+
+static struct sockaddr_in iq_addr;
+static int iq_addr_length;
+
+static struct sockaddr_in data_addr;
+static int data_addr_length;
+
+static pthread_t new_protocol_thread_id;
+static pthread_t new_protocol_timer_thread_id;
+
+sem_t response_sem;
+
+static long rx_sequence = 0;
+
+static long high_priority_sequence = 0;
+static long general_sequence = 0;
+static long rx_specific_sequence = 0;
+static long tx_specific_sequence = 0;
+
+static int lt2208Dither = 0;
+static int lt2208Random = 0;
+static int attenuation = 20; // 20dB
+
+static long long ddsAFrequency = 14250000;
+static int filterLow=150;
+static int filterHigh=2550;
+static int mode=modeUSB;
+static unsigned long alex_rx_antenna=0;
+static unsigned long alex_tx_antenna=0;
+static unsigned long alex_attenuation=0;
+
+static int buffer_size=BUFFER_SIZE;
+static int fft_size=4096;
+static int sampleRate=48000;
+static int dspRate=48000;
+static int outputRate=48000;
+
+static int micSampleRate=48000;
+static int micDspRate=48000;
+static int micOutputRate=192000;
+
+static int spectrumWIDTH=800;
+static int SPECTRUM_UPDATES_PER_SECOND=10;
+
+static int mox=0;
+static int tune=0;
+static float phase = 0.0F;
+
+static int ptt;
+static int dot;
+static int dash;
+static int pll_locked;
+static int adc_overload;
+unsigned int exciter_power;
+unsigned int alex_forward_power;
+unsigned int alex_reverse_power;
+static int supply_volts;
+
+long response_sequence;
+int response;
+
+int send_high_priority=0;
+int send_general=0;
+
+static void initAnalyzer(int channel,int buffer_size);
+static void* new_protocol_thread(void* arg);
+static void* new_protocol_timer_thread(void* arg);
+
+void filter_board_changed() {
+    send_general=1;
+}
+
+void pa_changed() {
+    send_general=1;
+}
+
+void tuner_changed() {
+    send_general=1;
+}
+
+void cw_changed() {
+}
+
+void set_attenuation(int value) {
+    attenuation=value;
+}
+
+int get_attenuation() {
+    return attenuation;
+}
+
+void set_alex_rx_antenna(unsigned long v) {
+    alex_rx_antenna=v;
+    send_high_priority=1;
+}
+
+void set_alex_tx_antenna(unsigned long v) {
+    alex_tx_antenna=v;
+    send_high_priority=1;
+}
+
+void set_alex_attenuation(unsigned long v) {
+    alex_attenuation=v;
+    send_high_priority=1;
+}
+
+void setSampleRate(int rate) {
+    sampleRate=rate;
+}
+
+int getSampleRate() {
+    return sampleRate;
+}
+
+void setFrequency(long long f) {
+    ddsAFrequency=f;
+    send_high_priority=1;
+}
+
+long long getFrequency() {
+    return ddsAFrequency;
+}
+
+void setMode(int m) {
+    mode=m;
+    SetRXAMode(receiver, mode);
+    SetTXAMode(CHANNEL_TX, mode);
+}
+
+int getMode() {
+    return mode;
+}
+
+void setFilter(int low,int high) {
+    if(mode==modeCWL) {
+        filterLow=-cwPitch-low;
+        filterHigh=-cwPitch+high;
+    } else if(mode==modeCWU) {
+        filterLow=cwPitch-low;
+        filterHigh=cwPitch+high;
+    } else {
+        filterLow=low;
+        filterHigh=high;
+    }
+
+    RXANBPSetFreqs(receiver,(double)filterLow,(double)filterHigh);
+    SetRXABandpassFreqs(receiver, (double)filterLow, (double)filterHigh);
+    SetRXASNBAOutputBandwidth(receiver, (double)filterLow, (double)filterHigh);
+
+    SetTXABandpassFreqs(CHANNEL_TX, (double)filterLow, (double)filterHigh);
+}
+
+int getFilterLow() {
+    return filterLow;
+}
+
+int getFilterHigh() {
+    return filterHigh;
+}
+
+void setMox(int state) {
+    if(mox!=state) {
+        mox=state;
+        send_high_priority=1;
+    }
+}
+
+int getMox() {
+    return mox;
+}
+
+void setTune(int state) {
+    if(tune!=state) {
+        tune=state;
+        send_high_priority=1;
+        send_general=1;
+    }
+}
+
+int getTune() {
+    return tune;
+}
+
+int isTransmitting() {
+    return ptt!=0 || mox!=0 || tune!=0;
+}
+
+double getDrive() {
+    return (double)drive/255.0;
+}
+
+void setDrive(double value) {
+    drive=(int)(value*255.0);
+    send_high_priority=1;
+}
+
+double getTuneDrive() {
+    return (double)tune_drive/255.0;
+}
+
+void setTuneDrive(double value) {
+    tune_drive=(int)(value*255.0);
+    send_high_priority=1;
+}
+
+void new_protocol_init(int rx,int pixels) {
+    int rc;
+    receiver=rx;
+    spectrumWIDTH=pixels;
+
+    fprintf(stderr,"new_protocol_init: %d\n",rx);
+
+    rc=sem_init(&response_sem, 0, 0);
+
+    fprintf(stderr,"OpenChannel %d buffer_size=%d fft_size=%d sampleRate=%d dspRate=%d outputRate=%d\n",
+                rx,
+                buffer_size,
+                fft_size,
+                sampleRate,
+                dspRate,
+                outputRate);
+
+    OpenChannel(rx,
+                buffer_size,
+                fft_size,
+                sampleRate,
+                dspRate,
+                outputRate,
+                0, // receive
+                1, // run
+                0.010, 0.025, 0.0, 0.010, 0);
+
+    fprintf(stderr,"OpenChannel %d buffer_size=%d fft_size=%d sampleRate=%d dspRate=%d outputRate=%d\n",
+                CHANNEL_TX,
+                buffer_size,
+                fft_size,
+                micSampleRate,
+                micDspRate,
+                micOutputRate);
+
+    OpenChannel(CHANNEL_TX,
+                buffer_size,
+                fft_size,
+                micSampleRate,
+                micDspRate,
+                micOutputRate,
+                1, // transmit
+                1, // run
+                0.010, 0.025, 0.0, 0.010, 0);
+
+    fprintf(stderr,"XCreateAnalyzer %d\n",rx);
+    int success;
+    XCreateAnalyzer(rx, &success, 262144, 1, 1, "");
+        if (success != 0) {
+            fprintf(stderr, "XCreateAnalyzer %d failed: %d\n" ,rx,success);
+        }
+    initAnalyzer(rx,buffer_size);
+
+    XCreateAnalyzer(CHANNEL_TX, &success, 262144, 1, 1, "");
+        if (success != 0) {
+            fprintf(stderr, "XCreateAnalyzer CHANNEL_TX failed: %d\n" ,success);
+        }
+    initAnalyzer(CHANNEL_TX,BUFFER_SIZE);
+
+    rc=pthread_create(&new_protocol_thread_id,NULL,new_protocol_thread,NULL);
+    if(rc != 0) {
+        fprintf(stderr,"pthread_create failed on new_protocol_thread for receiver %d: rc=%d\n", receiver, rc);
+        exit(-1);
+    }
+
+    SetRXAMode(rx, mode);
+    SetRXABandpassFreqs(rx, (double)filterLow, (double)filterHigh);
+    SetRXAAGCMode(rx, agc);
+    SetRXAAGCTop(rx,agc_gain);
+
+    SetRXAAMDSBMode(CHANNEL_RX0, 0);
+    SetRXAShiftRun(CHANNEL_RX0, 0);
+    SetRXAEMNRgainMethod(CHANNEL_RX0, 1);
+    SetRXAEMNRnpeMethod(CHANNEL_RX0, 0);
+    SetRXAEMNRaeRun(CHANNEL_RX0, 1);
+    SetRXAEMNRPosition(CHANNEL_RX0, 0);
+    SetRXAEMNRRun(CHANNEL_RX0, 0);
+    SetRXAANRRun(CHANNEL_RX0, 0);
+    SetRXAANFRun(CHANNEL_RX0, 0);
+
+    SetTXAMode(CHANNEL_TX, mode);
+    SetTXABandpassFreqs(CHANNEL_TX, (double)filterLow, (double)filterHigh);
+    SetTXABandpassWindow(CHANNEL_TX, 1);
+    SetTXABandpassRun(CHANNEL_TX, 1);
+
+    SetTXACFIRRun(CHANNEL_TX, 1);
+    SetTXAEQRun(CHANNEL_TX, 0);
+    SetTXACTCSSRun(CHANNEL_TX, 0);
+    SetTXAAMSQRun(CHANNEL_TX, 0);
+    SetTXACompressorRun(CHANNEL_TX, 0);
+    SetTXAosctrlRun(CHANNEL_TX, 0);
+    SetTXAPreGenRun(CHANNEL_TX, 0);
+    SetTXAPostGenRun(CHANNEL_TX, 0);
+
+}
+
+static void initAnalyzer(int channel,int buffer_size) {
+    int flp[] = {0};
+    double KEEP_TIME = 0.1;
+    int spur_elimination_ffts = 1;
+    int data_type = 1;
+    int fft_size = 8192;
+    int window_type = 4;
+    double kaiser_pi = 14.0;
+    int overlap = 2048;
+    int clip = 0;
+    int span_clip_l = 0;
+    int span_clip_h = 0;
+    int pixels=spectrumWIDTH;
+    int stitches = 1;
+    int avm = 0;
+    double tau = 0.001 * 120.0;
+    int MAX_AV_FRAMES = 60;
+    int display_average = MAX(2, (int) MIN((double) MAX_AV_FRAMES, (double) SPECTRUM_UPDATES_PER_SECOND * tau));
+    double avb = exp(-1.0 / (SPECTRUM_UPDATES_PER_SECOND * tau));
+    int calibration_data_set = 0;
+    double span_min_freq = 0.0;
+    double span_max_freq = 0.0;
+
+    int max_w = fft_size + (int) MIN(KEEP_TIME * (double) SPECTRUM_UPDATES_PER_SECOND, KEEP_TIME * (double) fft_size * (double) SPECTRUM_UPDATES_PER_SECOND);
+
+    fprintf(stderr,"SetAnalyzer channel=%d\n",channel);
+    SetAnalyzer(channel,
+            spur_elimination_ffts, //number of LO frequencies = number of ffts used in elimination
+            data_type, //0 for real input data (I only); 1 for complex input data (I & Q)
+            flp, //vector with one elt for each LO frequency, 1 if high-side LO, 0 otherwise
+            fft_size, //size of the fft, i.e., number of input samples
+            buffer_size, //number of samples transferred for each OpenBuffer()/CloseBuffer()
+            window_type, //integer specifying which window function to use
+            kaiser_pi, //PiAlpha parameter for Kaiser window
+            overlap, //number of samples each fft (other than the first) is to re-use from the previous
+            clip, //number of fft output bins to be clipped from EACH side of each sub-span
+            span_clip_l, //number of bins to clip from low end of entire span
+            span_clip_h, //number of bins to clip from high end of entire span
+            pixels, //number of pixel values to return.  may be either <= or > number of bins
+            stitches, //number of sub-spans to concatenate to form a complete span
+            avm, //averaging mode
+            display_average, //number of spans to (moving) average for pixel result
+            avb, //back multiplier for weighted averaging
+            calibration_data_set, //identifier of which set of calibration data to use
+            span_min_freq, //frequency at first pixel value8192
+            span_max_freq, //frequency at last pixel value
+            max_w //max samples to hold in input ring buffers
+    );
+}
+
+
+static void new_protocol_general() {
+    unsigned char buffer[60];
+
+fprintf(stderr,"new_protocol_general: receiver=%d\n", receiver);
+
+    memset(buffer, 0, sizeof(buffer));
+
+    buffer[0]=general_sequence>>24;
+    buffer[1]=general_sequence>>16;
+    buffer[2]=general_sequence>>8;
+    buffer[3]=general_sequence;
+
+    // use defaults apart from
+    buffer[37]=0x08;  //  phase word (not frequency)
+    buffer[58]=pa;  // enable PA 0x01
+    if((filter_board==APOLLO) && tune && apollo_tuner) {
+        buffer[58]|=0x02;
+    }
+
+    if(filter_board==ALEX) {
+        buffer[59]=0x01;  // enable Alex 0
+    }
+
+    if(sendto(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&base_addr,base_addr_length)<0) {
+        fprintf(stderr,"sendto socket failed for general\n");
+        exit(1);
+    }
+
+    general_sequence++;
+}
+
+static void new_protocol_high_priority(int run,int tx,int drive) {
+    unsigned char buffer[1444];
+
+//fprintf(stderr,"new_protocol_high_priority: run=%d tx=%d drive=%d tx_ant=0x%08x rx_ant=0x%08x\n", run, tx, drive, alex_tx_antenna, alex_rx_antenna);
+    memset(buffer, 0, sizeof(buffer));
+
+    buffer[0]=high_priority_sequence>>24;
+    buffer[1]=high_priority_sequence>>16;
+    buffer[2]=high_priority_sequence>>8;
+    buffer[3]=high_priority_sequence;
+
+    buffer[4]=run|(tx<<1);
+
+    extern long long ddsAFrequency;
+
+    long phase=(long)((4294967296.0*(double)ddsAFrequency)/122880000.0);
+
+// rx
+    buffer[9]=phase>>24;
+    buffer[10]=phase>>16;
+    buffer[11]=phase>>8;
+    buffer[12]=phase;
+
+// tx
+    buffer[329]=phase>>24;
+    buffer[330]=phase>>16;
+    buffer[331]=phase>>8;
+    buffer[332]=phase;
+
+
+    buffer[345]=drive;
+
+// alex HPF filters
+/*
+if              (frequency <  1800000) HPF <= 6'b100000;        // bypass
+else if (frequency <  6500000) HPF <= 6'b010000;        // 1.5MHz HPF   
+else if (frequency <  9500000) HPF <= 6'b001000;        // 6.5MHz HPF
+else if (frequency < 13000000) HPF <= 6'b000100;        // 9.5MHz HPF
+else if (frequency < 20000000) HPF <= 6'b000001;        // 13MHz HPF
+else                                               HPF <= 6'b000010;    // 20MHz HPF
+*/
+
+    long filters=0x00000000;
+// set HPF
+    if(ddsAFrequency<1800000L) {
+        filters|=ALEX_BYPASS_HPF;
+    } else if(ddsAFrequency<6500000L) {
+        filters|=ALEX_1_5MHZ_HPF;
+    } else if(ddsAFrequency<9500000L) {
+        filters|=ALEX_6_5MHZ_HPF;
+    } else if(ddsAFrequency<13000000L) {
+        filters|=ALEX_9_5MHZ_HPF;
+    } else if(ddsAFrequency<20000000L) {
+        filters|=ALEX_13MHZ_HPF;
+    } else {
+        filters|=ALEX_20MHZ_HPF;
+    }
+// alex LPF filters
+/*
+if (frequency > 32000000)   LPF <= 7'b0010000;             // > 10m so use 6m LPF^M
+else if (frequency > 22000000) LPF <= 7'b0100000;       // > 15m so use 12/10m LPF^M
+else if (frequency > 15000000) LPF <= 7'b1000000;       // > 20m so use 17/15m LPF^M
+else if (frequency > 8000000)  LPF <= 7'b0000001;       // > 40m so use 30/20m LPF  ^M
+else if (frequency > 4500000)  LPF <= 7'b0000010;       // > 80m so use 60/40m LPF^M
+else if (frequency > 2400000)  LPF <= 7'b0000100;       // > 160m so use 80m LPF  ^M
+else LPF <= 7'b0001000;             // < 2.4MHz so use 160m LPF^M
+*/
+
+    if(ddsAFrequency>32000000) {
+        filters|=ALEX_6_BYPASS_LPF;
+    } else if(ddsAFrequency>22000000) {
+        filters|=ALEX_12_10_LPF;
+    } else if(ddsAFrequency>15000000) {
+        filters|=ALEX_17_15_LPF;
+    } else if(ddsAFrequency>8000000) {
+        filters|=ALEX_30_20_LPF;
+    } else if(ddsAFrequency>4500000) {
+        filters|=ALEX_60_40_LPF;
+    } else if(ddsAFrequency>2400000) {
+        filters|=ALEX_80_LPF;
+    } else {
+        filters|=ALEX_160_LPF;
+    }
+
+    filters|=alex_rx_antenna;
+    filters|=alex_tx_antenna;
+    filters|=alex_attenuation;
+
+    if(tx) {
+        filters|=0x08000000;
+    }
+
+/*
+    buffer[1420]=filters>>24;
+    buffer[1421]=filters>>16;
+    buffer[1422]=filters>>8;
+    buffer[1423]=filters;
+*/
+
+    buffer[1432]=filters>>24;
+    buffer[1433]=filters>>16;
+    buffer[1434]=filters>>8;
+    buffer[1435]=filters;
+
+    //buffer[1442]=attenuation;
+    buffer[1443]=attenuation;
+
+//fprintf(stderr,"[4]=%02X\n", buffer[4]);
+//fprintf(stderr,"filters=%04X\n", filters);
+
+    if(sendto(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&high_priority_addr,high_priority_addr_length)<0) {
+        fprintf(stderr,"sendto socket failed for high priority\n");
+        exit(1);
+    }
+
+    high_priority_sequence++;
+}
+
+static void new_protocol_transmit_specific() {
+    unsigned char buffer[60];
+
+    memset(buffer, 0, sizeof(buffer));
+
+    buffer[0]=tx_specific_sequence>>24;
+    buffer[1]=tx_specific_sequence>>16;
+    buffer[2]=tx_specific_sequence>>8;
+    buffer[3]=tx_specific_sequence;
+
+    buffer[4]=1; // 1 DAC
+    buffer[5]=0; // no CW
+    if(cw_keyer_internal) {
+        buffer[5]|=0x02;
+    }
+    if(cw_keys_reversed) {
+        buffer[5]|=0x04;
+    }
+    if(cw_keyer_mode==KEYER_MODE_A) {
+        buffer[5]|=0x08;
+    }
+    if(cw_keyer_mode==KEYER_MODE_B) {
+        buffer[5]|=0x28;
+    }
+    if(cw_keyer_spacing) {
+        buffer[5]|=0x40;
+    }
+    if(cw_breakin) {
+        buffer[5]|=0x80;
+    }
+
+    buffer[6]=cw_keyer_sidetone_volume; // sidetone off
+    buffer[7]=cw_keyer_sidetone_frequency>>8; buffer[8]=cw_keyer_sidetone_frequency; // sidetone frequency
+    buffer[9]=cw_keyer_speed; // cw keyer speed
+    buffer[10]=cw_keyer_weight; // cw weight
+    buffer[11]=cw_keyer_hang_time>>8; buffer[12]=cw_keyer_hang_time; // cw hang delay
+    buffer[13]=0; // rf delay
+    buffer[50]=orion_mic;
+    buffer[51]=0x7F; // Line in gain
+
+    if(sendto(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&transmitter_addr,transmitter_addr_length)<0) {
+        fprintf(stderr,"sendto socket failed for tx specific\n");
+        exit(1);
+    }
+
+    tx_specific_sequence++;
+
+}
+
+static void new_protocol_receive_specific() {
+    unsigned char buffer[1444];
+    int i;
+
+    memset(buffer, 0, sizeof(buffer));
+
+    buffer[0]=rx_specific_sequence>>24;
+    buffer[1]=rx_specific_sequence>>16;
+    buffer[2]=rx_specific_sequence>>8;
+    buffer[3]=rx_specific_sequence;
+
+    buffer[4]=2; // 2 ADCs
+    buffer[5]=0; // dither off
+    if(lt2208Dither) {
+        buffer[5]=0x01<<receiver;
+    }
+    buffer[6]=0; // random off
+    if(lt2208Random) {
+        buffer[6]=0x01<<receiver;
+    }
+    buffer[7]=0x00;
+    for(i=0;i<receivers;i++) {
+      buffer[7]|=(1<<i);
+    }
+       
+    for(i=0;i<receivers;i++) {
+      buffer[17+(i*6)]=adc[i];
+    }
+
+    buffer[18]=((sampleRate/1000)>>8)&0xFF;
+    buffer[19]=(sampleRate/1000)&0xFF;
+    buffer[22]=24;
+
+    buffer[24]=((sampleRate/1000)>>8)&0xFF;
+    buffer[25]=(sampleRate/1000)&0xFF;
+    buffer[28]=24;
+
+    if(sendto(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&receiver_addr,receiver_addr_length)<0) {
+        fprintf(stderr,"sendto socket failed for start: receiver=%d\n",receiver);
+        exit(1);
+    }
+
+    rx_specific_sequence++;
+}
+
+static void new_protocol_start() {
+    running=1;
+    new_protocol_transmit_specific();
+    new_protocol_receive_specific();
+    int rc=pthread_create(&new_protocol_timer_thread_id,NULL,new_protocol_timer_thread,NULL);
+    if(rc != 0) {
+        fprintf(stderr,"pthread_create failed on new_protocol_timer_thread: %d\n", rc);
+        exit(-1);
+    }
+}
+
+void new_protocol_stop() {
+    new_protocol_high_priority(0,0,0);
+    running=0;
+    sleep(1);
+}
+
+/*
+float sineWave(float* buf, int samples, float phase, float freq) {
+    float phase_step = 2 * PI * freq / 192000.0F;
+    int i;
+    for (i = 0; i < samples; i++) {
+        buf[i] = (float) sin(phase);
+        phase += phase_step;
+    }
+    return phase;
+}
+*/
+float sineWave(double* buf, int samples, float phase, float freq) {
+    float phase_step = 2 * PI * freq / 192000.0F;
+    int i;
+    for (i = 0; i < samples; i++) {
+        buf[i*2] = (double) sin(phase);
+        phase += phase_step;
+    }
+    return phase;
+}
+
+double calibrate(int v) {
+    // Angelia
+    double v1;
+    v1=(double)v/4095.0*3.3;
+
+    return (v1*v1)/0.095;
+}
+
+void* new_protocol_thread(void* arg) {
+
+    DISCOVERED* d=&discovered[selected_device];
+
+    struct sockaddr_in addr;
+    int length;
+    unsigned char buffer[2048];
+    int bytesread;
+    short sourceport;
+
+    long sequence;
+    long long timestamp;
+    int bitspersample;
+    int samplesperframe;
+
+    int b;
+    int leftsample;
+    int rightsample;
+    float leftsamplefloat;
+    float rightsamplefloat;
+
+    int previous_ptt;
+    int previous_dot;
+    int previous_dash;
+
+    int samples;
+    //float leftinputbuffer[BUFFER_SIZE];
+    //float rightinputbuffer[BUFFER_SIZE];
+    double iqinputbuffer[BUFFER_SIZE*2];
+
+    int outputsamples;
+    //float leftoutputbuffer[BUFFER_SIZE];
+    //float rightoutputbuffer[BUFFER_SIZE];
+    double audiooutputbuffer[BUFFER_SIZE*2];
+
+    short leftaudiosample;
+    short rightaudiosample;
+    long audiosequence;
+    unsigned char audiobuffer[1444];
+    int audioindex;
+
+    int micsample;
+    float micsamplefloat;
+
+    int micsamples;
+/*
+    float micleftinputbuffer[BUFFER_SIZE];  // 48000
+    float micrightinputbuffer[BUFFER_SIZE];
+*/
+    double micinputbuffer[BUFFER_SIZE*2];
+
+    int micoutputsamples;
+/*
+    float micleftoutputbuffer[BUFFER_SIZE*4]; // 192000
+    float micrightoutputbuffer[BUFFER_SIZE*4];
+*/
+    double micoutputbuffer[BUFFER_SIZE*4*2];
+
+    int isample;
+    int qsample;
+    long tx_iq_sequence;
+    unsigned char iqbuffer[1444];
+    int iqindex;
+
+    int i, j;
+fprintf(stderr,"new_protocol_thread: receiver=%d\n", receiver);
+
+    micsamples=0;
+    iqindex=4;
+
+    switch(sampleRate) {
+        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;
+    }
+
+    micoutputsamples=BUFFER_SIZE*4;  // 48000 in, 192000 out
+
+fprintf(stderr,"outputsamples=%d\n", outputsamples);
+    data_socket=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
+    if(data_socket<0) {
+        fprintf(stderr,"metis: create socket failed for data_socket: receiver=%d\n",receiver);
+        exit(-1);
+    }
+
+    int optval = 1;
+    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) {
+        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;
+    base_addr.sin_port=htons(GENERAL_REGISTERS_FROM_HOST_PORT);
+
+    memcpy(&receiver_addr,&d->address,d->address_length);
+    receiver_addr_length=d->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;
+    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;
+    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;
+    audio_addr.sin_port=htons(AUDIO_FROM_HOST_PORT);
+
+    memcpy(&iq_addr,&d->address,d->address_length);
+    iq_addr_length=d->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;
+    data_addr.sin_port=htons(RX_IQ_TO_HOST_PORT+receiver);
+
+    samples=0;
+    audioindex=4; // leave space for sequence
+    audiosequence=0L;
+
+    new_protocol_general();
+    new_protocol_start();
+    new_protocol_high_priority(1,0,drive);
+
+    while(running) {
+        bytesread=recvfrom(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&addr,&length);
+        if(bytesread<0) {
+            fprintf(stderr,"recvfrom socket failed for new_protocol_thread: receiver=%d", receiver);
+            exit(1);
+        }
+
+        short sourceport=ntohs(addr.sin_port);
+
+//fprintf(stderr,"received packet length %d from port %d\n",bytesread,sourceport);
+
+        if(sourceport==RX_IQ_TO_HOST_PORT) {
+
+          sequence=((buffer[0]&0xFF)<<24)+((buffer[1]&0xFF)<<16)+((buffer[2]&0xFF)<<8)+(buffer[3]&0xFF);
+          timestamp=((long long)(buffer[4]&0xFF)<<56)+((long long)(buffer[5]&0xFF)<<48)+((long long)(buffer[6]&0xFF)<<40)+((long long)(buffer[7]&0xFF)<<32);
+                    ((long long)(buffer[8]&0xFF)<<24)+((long long)(buffer[9]&0xFF)<<16)+((long long)(buffer[10]&0xFF)<<8)+(long long)(buffer[11]&0xFF);
+          bitspersample=((buffer[12]&0xFF)<<8)+(buffer[13]&0xFF);
+          samplesperframe=((buffer[14]&0xFF)<<8)+(buffer[15]&0xFF);
+
+//fprintf(stderr,"samples per frame %d\n",samplesperframe);
+
+          //if(!isTransmitting()) {
+              b=16;
+              for(i=0;i<samplesperframe;i++) {
+                  leftsample   = (int)((signed char) buffer[b++]) << 16;
+                  leftsample  += (int)((unsigned char)buffer[b++]) << 8;
+                  leftsample  += (int)((unsigned char)buffer[b++]);
+                  rightsample  = (int)((signed char) buffer[b++]) << 16;
+                  rightsample += (int)((unsigned char)buffer[b++]) << 8;
+                  rightsample += (int)((unsigned char)buffer[b++]);
+
+                  leftsamplefloat=(float)leftsample/8388607.0; // for 24 bits
+                  rightsamplefloat=(float)rightsample/8388607.0; // for 24 bits
+            
+                  //leftinputbuffer[samples]=leftsamplefloat;
+                  //rightinputbuffer[samples]=rightsamplefloat;
+                  iqinputbuffer[samples*2]=(double)leftsamplefloat;
+                  iqinputbuffer[(samples*2)+1]=(double)rightsamplefloat;
+
+                  samples++;
+                  if(samples==BUFFER_SIZE) {
+                      int error;
+                      fexchange0(CHANNEL_RX0+receiver, iqinputbuffer, audiooutputbuffer, &error);
+                      if(error!=0) {
+                          fprintf(stderr,"fexchange0 returned error: %d for receiver %d\n", error,receiver);
+                      }
+
+                      Spectrum0(1, CHANNEL_RX0+receiver, 0, 0, iqinputbuffer);
+
+                      for(j=0;j<outputsamples;j++) {
+                        leftaudiosample=(short)(audiooutputbuffer[j*2]*32767.0*volume);
+                        rightaudiosample=(short)(audiooutputbuffer[(j*2)+1]*32767.0*volume);
+
+                        audiobuffer[audioindex++]=leftaudiosample>>8;
+                        audiobuffer[audioindex++]=leftaudiosample;
+                        audiobuffer[audioindex++]=rightaudiosample>>8;
+                        audiobuffer[audioindex++]=rightaudiosample;
+
+                        if(audioindex>=sizeof(audiobuffer)) {
+                            // insert the sequence
+                            audiobuffer[0]=audiosequence>>24;
+                            audiobuffer[1]=audiosequence>>16;
+                            audiobuffer[2]=audiosequence>>8;
+                            audiobuffer[3]=audiosequence;
+                            // send the buffer
+                            if(sendto(data_socket,audiobuffer,sizeof(audiobuffer),0,(struct sockaddr*)&audio_addr,audio_addr_length)<0) {
+                                fprintf(stderr,"sendto socket failed for audio\n");
+                                exit(1);
+                            }
+                            audioindex=4;
+                            audiosequence++;
+                        }
+                      }
+                      samples=0;
+                  }
+              }
+          //}
+
+       } else if(sourceport==COMMAND_RESPONCE_TO_HOST_PORT) {
+          // command/response
+          response_sequence=((buffer[0]&0xFF)<<24)+((buffer[1]&0xFF)<<16)+((buffer[2]&0xFF)<<8)+(buffer[3]&0xFF);
+          response=buffer[4]&0xFF;
+          fprintf(stderr,"response_sequence=%ld response=%d\n",response_sequence,response);
+          sem_post(&response_sem);
+
+       } else if(sourceport==HIGH_PRIORITY_TO_HOST_PORT) {
+          // high priority
+          sequence=((buffer[0]&0xFF)<<24)+((buffer[1]&0xFF)<<16)+((buffer[2]&0xFF)<<8)+(buffer[3]&0xFF);
+
+          previous_ptt=ptt;
+          previous_dot=dot;
+          previous_dash=dash;
+
+          ptt=buffer[4]&0x01;
+          dot=(buffer[4]>>1)&0x01;
+          dash=(buffer[4]>>2)&0x01;
+          pll_locked=(buffer[4]>>3)&0x01;
+          adc_overload=buffer[5]&0x01;
+          exciter_power=((buffer[6]&0xFF)<<8)|(buffer[7]&0xFF);
+          alex_forward_power=((buffer[14]&0xFF)<<8)|(buffer[15]&0xFF);
+          alex_reverse_power=((buffer[22]&0xFF)<<8)|(buffer[23]&0xFF);
+          supply_volts=((buffer[49]&0xFF)<<8)|(buffer[50]&0xFF);
+
+          if(previous_ptt!=ptt) {
+              //send_high_priority=1;
+              previous_ptt=ptt;
+              g_idle_add(ptt_update,(gpointer)ptt);
+          }
+
+       } else if(sourceport==MIC_LINE_TO_HOST_PORT) {
+          // Mic/Line data
+          sequence=((buffer[0]&0xFF)<<24)+((buffer[1]&0xFF)<<16)+((buffer[2]&0xFF)<<8)+(buffer[3]&0xFF);
+          if(isTransmitting()) {
+              b=4;
+
+              for(i=0;i<720;i++) {
+                  if(byte_swap) {
+                      micsample  = (int)((unsigned char)buffer[b++] & 0xFF);
+                      micsample  |= (int)((signed char) buffer[b++]) << 8;
+                  } else {
+                      micsample  = (int)((signed char) buffer[b++]) << 8;
+                      micsample  |= (int)((unsigned char)buffer[b++] & 0xFF);
+                  }
+                  micsamplefloat = (float)micsample/32767.0F; // 16 bit sample
+
+                  micinputbuffer[micsamples*2]=(double)(micsamplefloat*mic_gain);
+                  micinputbuffer[(micsamples*2)+1]=0.0;
+
+                  micsamples++;
+
+                  if(micsamples==BUFFER_SIZE) {
+                      int error;
+
+                      if(tune==1) {
+                          //float tunefrequency = (float)((filterLow + filterHigh - filterLow) / 2);
+                          float tunefrequency = (float)((filterHigh - filterLow) / 2);
+                          phase=sineWave(micinputbuffer, BUFFER_SIZE, phase, tunefrequency);
+                      }
+
+                      fexchange0(CHANNEL_TX, micinputbuffer, micoutputbuffer, &error);
+                      if(error!=0) {
+                          fprintf(stderr,"fexchange0 returned error: %d for transmitter\n", error);
+                      }
+
+                      for(j=0;j<micoutputsamples;j++) {
+                        isample=(int)(micoutputbuffer[j*2]*8388607.0); // 24 bit
+                        qsample=(int)(micoutputbuffer[(j*2)+1]*8388607.0); // 24 bit
+
+                        iqbuffer[iqindex++]=isample>>16;
+                        iqbuffer[iqindex++]=isample>>8;
+                        iqbuffer[iqindex++]=isample;
+                        iqbuffer[iqindex++]=qsample>>16;
+                        iqbuffer[iqindex++]=qsample>>8;
+                        iqbuffer[iqindex++]=qsample;
+
+                        if(iqindex>=sizeof(iqbuffer)) {
+                            // insert the sequence
+                            iqbuffer[0]=tx_iq_sequence>>24;
+                            iqbuffer[1]=tx_iq_sequence>>16;
+                            iqbuffer[2]=tx_iq_sequence>>8;
+                            iqbuffer[3]=tx_iq_sequence;
+
+                            // send the buffer
+                            if(sendto(data_socket,iqbuffer,sizeof(iqbuffer),0,(struct sockaddr*)&iq_addr,iq_addr_length)<0) {
+                                fprintf(stderr,"sendto socket failed for iq\n");
+                                exit(1);
+                            }
+                            iqindex=4;
+                            tx_iq_sequence++;
+                        }
+
+                      }
+                      //Spectrum(CHANNEL_TX, 0, 0, micrightoutputbuffer, micleftoutputbuffer);
+                      Spectrum0(1, CHANNEL_TX, 0, 0, micoutputbuffer);
+                      micsamples=0;
+                  }
+#ifdef ECHO_MIC
+                  audiobuffer[audioindex++]=micsample>>8;
+                  audiobuffer[audioindex++]=micsample;
+                  audiobuffer[audioindex++]=micsample>>8;
+                  audiobuffer[audioindex++]=micsample;
+
+                  if(audioindex>=sizeof(audiobuffer)) {
+                      // insert the sequence
+                      audiobuffer[0]=audiosequence>>24;
+                      audiobuffer[1]=audiosequence>>16;
+                      audiobuffer[2]=audiosequence>>8;
+                      audiobuffer[3]=audiosequence;
+                      // send the buffer
+                      if(sendto(data_socket,audiobuffer,sizeof(audiobuffer),0,(struct sockaddr*)&audio_addr,audio_addr_length)<0) {
+                          fprintf(stderr,"sendto socket failed for audio\n");
+                          exit(1);
+                      }
+                      audioindex=4;
+                      audiosequence++;
+                  }
+#endif
+              }
+          }
+       } else {
+       }
+
+       if(running) {
+           if(send_general==1) {
+               new_protocol_general();
+               send_general=0;
+           }
+           if(send_high_priority==1) {
+               new_protocol_high_priority(1,isTransmitting(),tune==0?drive:tune_drive);
+               send_high_priority=0;
+           }
+       }
+        
+    }
+
+    close(data_socket);
+}
+
+void* new_protocol_timer_thread(void* arg) {
+    int count=0;
+fprintf(stderr,"new_protocol_timer_thread\n");
+    while(running) {
+        usleep(100000); // 100ms
+        if(running) {
+            if(count==0) {
+                new_protocol_transmit_specific();
+                count=1;
+            } else {
+                new_protocol_receive_specific();
+                count=0;
+            }
+        }
+    }
+}
diff --git a/new_protocol.h b/new_protocol.h
new file mode 100644 (file)
index 0000000..1252f65
--- /dev/null
@@ -0,0 +1,78 @@
+
+// port definitions from host
+#define GENERAL_REGISTERS_FROM_HOST_PORT 1024
+#define PROGRAMMING_FROM_HOST_PORT 1024
+#define RECEIVER_SPECIFIC_REGISTERS_FROM_HOST_PORT 1025
+#define TRANSMITTER_SPECIFIC_REGISTERS_FROM_HOST_PORT 1026
+#define HIGH_PRIORITY_FROM_HOST_PORT 1027
+#define AUDIO_FROM_HOST_PORT 1028
+#define TX_IQ_FROM_HOST_PORT 1029
+
+// port definitions to host
+#define COMMAND_RESPONCE_TO_HOST_PORT 1024
+#define HIGH_PRIORITY_TO_HOST_PORT 1025
+#define MIC_LINE_TO_HOST_PORT 1026
+#define WIDE_BAND_TO_HOST_PORT 1027
+#define RX_IQ_TO_HOST_PORT 1035
+
+#define BUFFER_SIZE 1024
+
+#define modeLSB 0
+#define modeUSB 1
+#define modeDSB 2
+#define modeCWL 3
+#define modeCWU 4
+#define modeFMN 5
+#define modeAM 6
+#define modeDIGU 7
+#define modeSPEC 8
+#define modeDIGL 9
+#define modeSAM 10
+#define modeDRM 11
+
+extern int data_socket;
+extern sem_t response_sem;
+
+extern long response_sequence;
+extern int response;
+
+extern unsigned int exciter_power;
+extern unsigned int alex_forward_power;
+extern unsigned int alex_reverse_power;
+
+void new_protocol_init(int rx,int pixels);
+void new_protocol_stop();
+
+void filter_board_changed();
+void pa_changed();
+void tuner_changed();
+void cw_changed();
+
+void set_attenuation(int value);
+int get_attenuation();
+
+void set_alex_rx_antenna(unsigned long v);
+void set_alex_tx_antenna(unsigned long v);
+void set_alex_attenuation(unsigned long v);
+
+void setMox(int state);
+int getMox();
+void setTune(int state);
+int getTune();
+int isTransmitting();
+
+double getDrive();
+void setDrive(double d);
+double getTuneDrive();
+void setTuneDrive(double d);
+
+void setSampleRate(int rate);
+int getSampleRate();
+void setFrequency(long long f);
+long long getFrequency();
+void setMode(int m);
+int getMode();
+void setFilter(int low,int high);
+int getFilterLow();
+int getFilterHigh();
+
diff --git a/new_protocol_programmer.c b/new_protocol_programmer.c
new file mode 100644 (file)
index 0000000..8d0dea0
--- /dev/null
@@ -0,0 +1,213 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <sys/stat.h>
+#include <ifaddrs.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <time.h>
+#include <math.h>
+#include <errno.h>
+
+
+#include "discovered.h"
+#include "new_protocol.h"
+#include "new_protocol.h"
+
+static long sequence;
+static long block_sequence;
+
+static struct sockaddr_in program_addr;
+static int program_addr_length;
+
+static char* source;
+static long blocks;
+
+static pthread_t programmer_thread_id;
+static pthread_t programmer_response_thread_id;
+
+static int running;
+
+void *programmer_thread(void *arg);
+void *programmer_response_thread(void *arg);
+
+void new_protocol_programmer(char *filename ) {
+    int rc;
+
+    fprintf(stderr,"new_protocol_programmer: %s\n",filename);
+
+    sequence=0L;
+    block_sequence=0L;
+
+    DISCOVERED* d=&discovered[selected_device];
+
+    memcpy(&program_addr,&d->address,d->address_length);
+    program_addr_length=d->address_length;
+    program_addr.sin_port=htons(PROGRAMMING_FROM_HOST_PORT);
+
+    FILE *fp;
+    long length;
+
+    fp=fopen(filename,"rb");
+
+    if(fp==NULL) {
+       fprintf(stderr, "file %s not found!\n", filename);
+       exit(-1);
+    }
+
+    fseek(fp,0L,SEEK_END);
+    length=ftell(fp);
+    fseek(fp,0L,SEEK_SET);
+    fprintf(stderr,"the file's length is %ld bytes\n",length);
+
+    long source_size=((length+511)/512)*512;
+    fprintf(stderr,"allocating %ld bytes\n",source_size);
+    source=(char *)malloc(source_size);
+
+    blocks=source_size/256;
+    // read the file in
+    int r;
+    int b=0;
+    while((r==fread(&source[b],sizeof(char),512, fp))>0) {
+        b+=r;
+    }
+
+    fclose(fp);
+
+    // start the thread to program 
+    rc=pthread_create(&programmer_thread_id,NULL,programmer_thread,NULL);
+    if(rc != 0) {
+        fprintf(stderr,"pthread_create failed on programmer_thread: rc=%d\n", rc);
+        exit(-1);
+    }
+
+}
+
+void programmer_erase() {
+    char buffer[60];
+
+    fprintf(stderr,"programmer_erase\n");
+    memset(buffer, 0, sizeof(buffer));
+
+    buffer[0]=sequence>>24;
+    buffer[1]=sequence>>16;
+    buffer[2]=sequence>>8;
+    buffer[3]=sequence;
+
+    buffer[4]=0x04; // Erase command
+
+    if(sendto(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&program_addr,program_addr_length)<0) {
+        fprintf(stderr,"sendto socket failed for erase\n");
+        exit(1);
+    }
+
+}
+
+void programmer_send_block(long block) {
+    char buffer[265];
+
+    fprintf(stderr,"programmer_send_block: %ld\n",block);
+
+    memset(buffer, 0, sizeof(buffer));
+
+    buffer[0]=block_sequence>>24;
+    buffer[1]=block_sequence>>16;
+    buffer[2]=block_sequence>>8;
+    buffer[3]=block_sequence;
+
+    buffer[4]=0x05; // Program command
+
+
+    buffer[5]=blocks>>24;
+    buffer[6]=blocks>>16;
+    buffer[7]=blocks>>8;
+    buffer[8]=blocks;
+    
+
+    // copy a block (256 bytes)
+    memcpy(&buffer[9],&source[block*256],256);
+
+    if(sendto(data_socket,buffer,sizeof(buffer),0,(struct sockaddr*)&program_addr,program_addr_length)<0) {
+        fprintf(stderr,"sendto socket failed for erase\n");
+        exit(1);
+    }
+}
+
+void *programmer_thread(void *arg) {
+
+    int result;
+    struct timespec ts;
+
+    fprintf(stderr,"programmer_thread\n");
+    programmer_erase();
+
+    // wait for the response to the erase command
+    clock_gettime(CLOCK_REALTIME, &ts);
+    ts.tv_sec+=120; // wait for 30 seconds
+    result=sem_timedwait(&response_sem,&ts);
+    if(result==-1) {
+        if(errno == ETIMEDOUT) {
+            fprintf(stderr,"timedout waiting for response for erase (start)\n");
+            exit(1);
+        } else {
+            fprintf(stderr,"sem_timedwait failed for response for erase (start): %d\n",errno);
+            exit(1);
+        }
+    }
+    if(response!=0x03) {
+        fprintf(stderr,"response error %d for erase (start)\n", response);
+        exit(1);
+    }
+
+    // wait for the erase to complete
+    clock_gettime(CLOCK_REALTIME, &ts);
+    ts.tv_sec+=120; // wait for 30 seconds
+    result=sem_timedwait(&response_sem,&ts);
+    if(result==-1) {
+        if(errno == ETIMEDOUT) {
+            fprintf(stderr,"timedout waiting for response for erase (complete)\n");
+            exit(1);
+        } else {
+            fprintf(stderr,"sem_timedwait failed for response for erase (complete): %d\n",errno);
+            exit(1);
+        }
+    }
+    if(response!=0x03) {
+        fprintf(stderr,"response error %d for erase (complete)\n", response);
+        exit(1);
+    }
+
+    long b=0;
+
+    while(b<blocks) {
+        programmer_send_block(b);
+        clock_gettime(CLOCK_REALTIME, &ts);
+        ts.tv_sec+=5; // wait for 5 seconds
+        result=sem_timedwait(&response_sem,&ts);
+        if(result==-1) {
+            if(errno == ETIMEDOUT) {
+                fprintf(stderr,"timedout waiting for response for sent block\n");
+                exit(1);
+            } else {
+                fprintf(stderr,"sem_timedwait failed for response for sent block: %d\n",errno);
+                exit(1);
+            }
+        }
+        if(response!=0x04) {
+            fprintf(stderr,"response error %d waiting for sent block\n", response);
+            exit(1);
+        }
+        block_sequence++;
+    }
+    
+}
diff --git a/panadapter.c b/panadapter.c
new file mode 100644 (file)
index 0000000..96284ef
--- /dev/null
@@ -0,0 +1,314 @@
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <math.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <semaphore.h>
+#include "new_protocol.h"
+#include "panadapter.h"
+#include "vfo.h"
+
+static GtkWidget *panadapter;
+static cairo_surface_t *panadapter_surface = NULL;
+
+static float* samples;
+
+static gint last_x;
+static gboolean has_moved=FALSE;
+static gboolean pressed=FALSE;
+
+static float panadapter_max=-60.0;
+static float panadapter_min=-160.0;
+
+static gfloat hz_per_pixel;
+static gfloat filter_left;
+static gfloat filter_right;
+
+static int display_width;
+static int panadapter_height;
+
+static void
+panadapter_clear_surface (void)
+{
+  cairo_t *cr;
+
+  cr = cairo_create (panadapter_surface);
+
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  cairo_paint (cr);
+
+  cairo_destroy (cr);
+}
+
+/* Create a new surface of the appropriate size to store our scribbles */
+static gboolean
+panadapter_configure_event_cb (GtkWidget         *widget,
+            GdkEventConfigure *event,
+            gpointer           data)
+{
+  if (panadapter_surface)
+    cairo_surface_destroy (panadapter_surface);
+
+  panadapter_surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
+                                       CAIRO_CONTENT_COLOR,
+                                       gtk_widget_get_allocated_width (widget),
+                                       gtk_widget_get_allocated_height (widget));
+
+  /* Initialize the surface to white */
+  panadapter_clear_surface ();
+
+  /* We've handled the configure event, no need for further processing. */
+  return TRUE;
+}
+
+/* Redraw the screen from the surface. Note that the ::draw
+ * signal receives a ready-to-be-used cairo_t that is already
+ * clipped to only draw the exposed areas of the widget
+ */
+static gboolean
+panadapter_draw_cb (GtkWidget *widget,
+ cairo_t   *cr,
+ gpointer   data)
+{
+  cairo_set_source_surface (cr, panadapter_surface, 0, 0);
+  cairo_paint (cr);
+
+  return FALSE;
+}
+
+static gboolean
+panadapter_button_press_event_cb (GtkWidget      *widget,
+               GdkEventButton *event,
+               gpointer        data)
+{
+  int x=(int)event->x;
+  if (event->button == 1) {
+    last_x=(int)event->x;
+    has_moved=FALSE;
+    pressed=TRUE;
+  }
+  return TRUE;
+}
+
+static gboolean
+panadapter_button_release_event_cb (GtkWidget      *widget,
+               GdkEventButton *event,
+               gpointer        data)
+{
+  int x=(int)event->x;
+  if (event->button == 1) {
+    if(has_moved) {
+      // drag
+      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));
+    }
+    last_x=x;
+    pressed=FALSE;
+  }
+  return TRUE;
+}
+
+static gboolean
+panadapter_motion_notify_event_cb (GtkWidget      *widget,
+                GdkEventMotion *event,
+                gpointer        data)
+{
+  int x, y;
+  GdkModifierType state;
+  gdk_window_get_device_position (event->window,
+                                event->device,
+                                &x,
+                                &y,
+                                &state);
+  if((state & GDK_BUTTON1_MASK == GDK_BUTTON1_MASK) || pressed) {
+    int moved=last_x-x;
+    vfo_move((int)((float)moved*hz_per_pixel));
+    last_x=x;
+    has_moved=TRUE;
+  }
+
+  return TRUE;
+}
+
+static void
+close_window (void)
+{
+  if (panadapter_surface)
+    cairo_surface_destroy (panadapter_surface);
+
+  gtk_main_quit ();
+}
+
+void panadapter_update(float *data,int tx) {
+    int i;
+    int result;
+    
+    float saved_max;
+    float saved_min;
+    gfloat saved_hz_per_pixel;
+
+    samples=data;
+    //if(result==1) {
+        if(panadapter_surface) {
+
+            if(tx) {
+                saved_max=panadapter_max;
+                saved_min=panadapter_min;
+                saved_hz_per_pixel=hz_per_pixel;
+
+                panadapter_max=30;
+                panadapter_min=-100;
+                hz_per_pixel=192000.0/(double)display_width;
+            }
+
+            //clear_panadater_surface();
+            cairo_t *cr;
+            cr = cairo_create (panadapter_surface);
+            cairo_set_source_rgb (cr, 0, 0, 0);
+            cairo_paint (cr);
+
+            // 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);
+            cairo_rectangle(cr, filter_left, 0.0, filter_right-filter_left, (double)panadapter_height);
+            cairo_fill(cr);
+
+            // plot the levels
+            int V = (int)(panadapter_max - panadapter_min);
+            int numSteps = V / 20;
+            for (i = 1; i < numSteps; i++) {
+                int num = panadapter_max - i * 20;
+                int y = (int)floor((panadapter_max - num) * panadapter_height / V);
+
+                cairo_set_source_rgb (cr, 0, 1, 1);
+                cairo_set_line_width(cr, 1.0);
+                cairo_move_to(cr,0.0,(double)y);
+                cairo_line_to(cr,(double)display_width,(double)y);
+
+                cairo_set_source_rgb (cr, 0, 1, 1);
+                cairo_select_font_face(cr, "Arial",
+                    CAIRO_FONT_SLANT_NORMAL,
+                    CAIRO_FONT_WEIGHT_BOLD);
+                cairo_set_font_size(cr, 12);
+                char v[32];
+                sprintf(v,"%d dBm",num);
+                cairo_move_to(cr, 1, (double)y);  
+                cairo_show_text(cr, v);
+            }
+            cairo_stroke(cr);
+
+            // plot frequency markers
+            long f;
+            for(i=0;i<display_width;i++) {
+                f = getFrequency() - ((long) getSampleRate() / 2) + (long) (hz_per_pixel * i);
+                if (f > 0) {
+                    if ((f % 20000) < (long) hz_per_pixel) {
+                        cairo_set_source_rgb (cr, 0, 1, 1);
+                        cairo_set_line_width(cr, 1.0);
+                        cairo_move_to(cr,(double)i,0.0);
+                        cairo_line_to(cr,(double)i,(double)panadapter_height);
+
+                        cairo_set_source_rgb (cr, 0, 1, 1);
+                        cairo_select_font_face(cr, "Arial",
+                            CAIRO_FONT_SLANT_NORMAL,
+                            CAIRO_FONT_WEIGHT_BOLD);
+                        cairo_set_font_size(cr, 12);
+                        char v[32];
+                        sprintf(v,"%0ld.%03ld",f/1000000,(f%1000000)/1000);
+                        cairo_move_to(cr, (double)i, (double)(panadapter_height-10));  
+                        cairo_show_text(cr, v);
+                    }
+                }
+            }
+            cairo_stroke(cr);
+
+            // cursor
+            cairo_set_source_rgb (cr, 1, 0, 0);
+            cairo_set_line_width(cr, 1.0);
+            cairo_move_to(cr,(double)display_width/2.0,0.0);
+            cairo_line_to(cr,(double)display_width/2.0,(double)panadapter_height);
+            cairo_stroke(cr);
+
+            // signal
+            cairo_set_source_rgb(cr, 1, 1, 1);
+            cairo_set_line_width(cr, 1.0);
+
+            double s1,s2;
+            samples[0]=panadapter_min-20;
+            samples[display_width-1]=panadapter_min-20;
+            for(i=1;i<display_width;i++) {
+                s1=samples[i-1]+get_attenuation();
+                s1 = floor((panadapter_max - s1)
+                            * (double) panadapter_height
+                            / (panadapter_max - panadapter_min));
+                s2=samples[i]+get_attenuation();
+                s2 = floor((panadapter_max - s2)
+                            * (double) panadapter_height
+                            / (panadapter_max - panadapter_min));
+                cairo_move_to(cr, (double)i-1, s1);
+                cairo_line_to(cr, (double)i, s2);
+            }
+            cairo_stroke(cr);
+            //cairo_close_path(cr);
+            //cairo_fill(cr);
+
+            cairo_destroy (cr);
+            gtk_widget_queue_draw (panadapter);
+
+            if(tx) {
+                panadapter_max=saved_max;
+                panadapter_min=saved_min;
+                hz_per_pixel=saved_hz_per_pixel;
+            }
+
+        }
+    //}
+}
+
+GtkWidget* panadapter_init(int width,int height) {
+  //GtkWidget *panadapter_frame;
+
+  display_width=width;
+  panadapter_height=height;
+
+  samples=malloc(display_width*sizeof(float));
+  hz_per_pixel=(double)getSampleRate()/(double)display_width;
+
+  //panadapter_frame = gtk_frame_new (NULL);
+  panadapter = gtk_drawing_area_new ();
+  gtk_widget_set_size_request (panadapter, width, height);
+
+  //gtk_container_add (GTK_CONTAINER (panadapter_frame), panadapter);
+
+  /* Signals used to handle the backing surface */
+  g_signal_connect (panadapter, "draw",
+            G_CALLBACK (panadapter_draw_cb), NULL);
+  g_signal_connect (panadapter,"configure-event",
+            G_CALLBACK (panadapter_configure_event_cb), NULL);
+
+  /* Event signals */
+  g_signal_connect (panadapter, "motion-notify-event",
+            G_CALLBACK (panadapter_motion_notify_event_cb), NULL);
+  g_signal_connect (panadapter, "button-press-event",
+            G_CALLBACK (panadapter_button_press_event_cb), NULL);
+  g_signal_connect (panadapter, "button-release-event",
+            G_CALLBACK (panadapter_button_release_event_cb), NULL);
+
+  /* Ask to receive events the drawing area doesn't normally
+   * subscribe to. In particular, we need to ask for the
+   * button press and motion notify events that want to handle.
+   */
+  gtk_widget_set_events (panadapter, gtk_widget_get_events (panadapter)
+                     | GDK_BUTTON_PRESS_MASK
+                     | GDK_BUTTON_RELEASE_MASK
+                     | GDK_BUTTON1_MOTION_MASK
+                     | GDK_POINTER_MOTION_MASK
+                     | GDK_POINTER_MOTION_HINT_MASK);
+
+  //return panadapter_frame;
+  return panadapter;
+}
diff --git a/panadapter.h b/panadapter.h
new file mode 100644 (file)
index 0000000..142ff31
--- /dev/null
@@ -0,0 +1,3 @@
+void panadapter_update(float* data,int tx);
+
+GtkWidget* panadapter_init(int width,int height);
diff --git a/property.c b/property.c
new file mode 100644 (file)
index 0000000..22bf486
--- /dev/null
@@ -0,0 +1,116 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "property.h"
+
+PROPERTY* properties;
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief Load Properties
+*
+* @param filename
+*/
+void loadProperties(char* filename) {
+    char string[80];
+    char* name;
+    char* value;
+    FILE* f=fopen(filename,"r");
+    properties=NULL;
+    PROPERTY* property;
+
+    fprintf(stderr,"loadProperties: %s\n",filename);
+    
+    if(f) {
+        while(fgets(string,sizeof(string),f)) {
+            if(string[0]!='#') {
+                name=strtok(string,"=");
+                value=strtok(NULL,"\n");
+                property=malloc(sizeof(PROPERTY));
+                property->name=malloc(strlen(name)+1);
+                strcpy(property->name,name);
+                property->value=malloc(strlen(value)+1);
+                strcpy(property->value,value);
+                property->next_property=properties;
+                properties=property;
+            }
+        }
+        fclose(f);
+    }
+}
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief Save Properties
+*
+* @param filename
+*/
+void saveProperties(char* filename) {
+    PROPERTY* property=properties;
+    FILE* f=fopen(filename,"w+");
+    char line[512];
+    if(!f) {
+        fprintf(stderr,"can't open %s\n",filename);
+        exit(1);
+    }
+    while(property) {
+        sprintf(line,"%s=%s\n",property->name,property->value);
+        fwrite(line,1,strlen(line),f);
+        property=property->next_property;
+    }
+    fclose(f);
+}
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief Get Properties
+*
+* @param name
+*
+* @return
+*/
+char* getProperty(char* name) {
+    char* value=NULL;
+    PROPERTY* property=properties;
+    while(property) {
+        if(strcmp(name,property->name)==0) {
+            value=property->value;
+            break;
+        }
+        property=property->next_property;
+    }
+    return value;
+}
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief Set Properties
+*
+* @param name
+* @param value
+*/
+void setProperty(char* name,char* value) {
+    PROPERTY* property=properties;
+    while(property) {
+        if(strcmp(name,property->name)==0) {
+            break;
+        }
+        property=property->next_property;
+    }
+    if(property) {
+        // just update
+        free(property->value);
+        property->value=malloc(strlen(value)+1);
+        strcpy(property->value,value);
+    } else {
+        // new property
+        property=malloc(sizeof(PROPERTY));
+        property->name=malloc(strlen(name)+1);
+        strcpy(property->name,name);
+        property->value=malloc(strlen(value)+1);
+        strcpy(property->value,value);
+        property->next_property=properties;
+        properties=property;
+    }
+}
+
diff --git a/property.h b/property.h
new file mode 100644 (file)
index 0000000..55cd608
--- /dev/null
@@ -0,0 +1,16 @@
+typedef struct _PROPERTY PROPERTY;
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief Property structure
+*/
+struct _PROPERTY {
+    char* name;
+    char* value;
+    PROPERTY* next_property;
+};
+
+void loadProperties(char* filename);
+char* getProperty(char* name);
+void setProperty(char* name,char* value);
+
diff --git a/radio.c b/radio.c
new file mode 100644 (file)
index 0000000..1188605
--- /dev/null
+++ b/radio.c
@@ -0,0 +1,199 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "radio.h"
+#include "agc.h"
+#include "band.h"
+#include "discovered.h"
+#include "property.h"
+
+char property_path[128];
+
+int sample_rate=48000;
+int filter_board=ALEX;
+int pa=PA_ENABLED;
+int apollo_tuner=0;
+
+int panadapter_high=-80;
+int panadapter_low=-160;
+
+int waterfall_high=-100;
+int waterfall_low=-150;
+int waterfall_automatic=1;
+
+double volume=0.2;
+double mic_gain=1.5;
+
+int orion_mic=MIC_BOOST|ORION_MIC_PTT_ENABLED|ORION_MIC_PTT_TIP_BIAS_RING|ORION_MIC_BIAS_ENABLED;
+
+int agc=AGC_MEDIUM;
+double agc_gain=60.0;
+
+int nr=0;
+int nb=0;
+int anf=0;
+int snb=0;
+
+int cwPitch=600;
+
+int tune_drive=6;
+int drive=60;
+
+int receivers=2;
+int adc[2]={0,1};
+
+int locked=0;
+
+int step=100;
+
+int byte_swap=0;
+
+int cw_keys_reversed=0; // 0=disabled 1=enabled
+int cw_keyer_speed=12; // 1-60 WPM
+int cw_keyer_mode=KEYER_STRAIGHT;
+int cw_keyer_weight=30; // 0-100
+int cw_keyer_spacing=0; // 0=on 1=off
+int cw_keyer_internal=1; // 0=external 1=internal
+int cw_keyer_sidetone_volume=127; // 0-127
+int cw_keyer_ptt_delay=20; // 0-255ms
+int cw_keyer_hang_time=300; // ms
+int cw_keyer_sidetone_frequency=400; // Hz
+int cw_breakin=1; // 0=disabled 1=enabled
+
+void radioRestoreState() {
+    char *value;
+    value=getProperty("sample_rate");
+    if(value) sample_rate=atoi(value);
+    value=getProperty("filter_board");
+    if(value) filter_board=atoi(value);
+    value=getProperty("apollo_tuner");
+    if(value) apollo_tuner=atoi(value);
+    value=getProperty("pa");
+    if(value) pa=atoi(value);
+    value=getProperty("panadapter_high");
+    if(value) panadapter_high=atoi(value);
+    value=getProperty("panadapter_low");
+    if(value) panadapter_low=atoi(value);
+    value=getProperty("waterfall_high");
+    if(value) waterfall_high=atoi(value);
+    value=getProperty("waterfall_low");
+    if(value) waterfall_low=atoi(value);
+    value=getProperty("waterfall_automatic");
+    if(value) waterfall_automatic=atoi(value);
+    value=getProperty("volume");
+    if(value) volume=atof(value);
+    value=getProperty("mic_gain");
+    if(value) mic_gain=atof(value);
+    value=getProperty("orion_mic");
+    if(value) orion_mic=atof(value);
+    value=getProperty("nr");
+    if(value) nr=atoi(value);
+    value=getProperty("nb");
+    if(value) nb=atoi(value);
+    value=getProperty("anf");
+    if(value) anf=atoi(value);
+    value=getProperty("snb");
+    if(value) snb=atoi(value);
+    value=getProperty("agc");
+    if(value) agc=atoi(value);
+    value=getProperty("agc_gain");
+    if(value) agc_gain=atof(value);
+    value=getProperty("step");
+    if(value) step=atoi(value);
+    value=getProperty("byte_swap");
+    if(value) byte_swap=atoi(value);
+    value=getProperty("cw_keys_reversed");
+    if(value) cw_keys_reversed=atoi(value);
+    value=getProperty("cw_keyer_speed");
+    if(value) cw_keyer_speed=atoi(value);
+    value=getProperty("cw_keyer_mode");
+    if(value) cw_keyer_mode=atoi(value);
+    value=getProperty("cw_keyer_weight");
+    if(value) cw_keyer_weight=atoi(value);
+    value=getProperty("cw_keyer_spacing");
+    if(value) cw_keyer_spacing=atoi(value);
+    value=getProperty("cw_keyer_internal");
+    if(value) cw_keyer_internal=atoi(value);
+    value=getProperty("cw_keyer_sidetone_volume");
+    if(value) cw_keyer_sidetone_volume=atoi(value);
+    value=getProperty("cw_keyer_ptt_delay");
+    if(value) cw_keyer_ptt_delay=atoi(value);
+    value=getProperty("cw_keyer_hang_time");
+    if(value) cw_keyer_hang_time=atoi(value);
+    value=getProperty("cw_keyer_sidetone_frequency");
+    if(value) cw_keyer_sidetone_frequency=atoi(value);
+    value=getProperty("cw_breakin");
+    if(value) cw_breakin=atoi(value);
+
+
+    bandRestoreState();
+}
+
+void radioSaveState() {
+    char value[80];
+    sprintf(value,"%d",sample_rate);
+    setProperty("sample_rate",value);
+    sprintf(value,"%d",filter_board);
+    setProperty("filter_board",value);
+    sprintf(value,"%d",apollo_tuner);
+    setProperty("apollo_tuner",value);
+    sprintf(value,"%d",pa);
+    setProperty("pa",value);
+    sprintf(value,"%d",panadapter_high);
+    setProperty("panadapter_high",value);
+    sprintf(value,"%d",panadapter_low);
+    setProperty("panadapter_low",value);
+    sprintf(value,"%d",waterfall_high);
+    setProperty("waterfall_high",value);
+    sprintf(value,"%d",waterfall_low);
+    setProperty("waterfall_low",value);
+    sprintf(value,"%d",waterfall_automatic);
+    setProperty("waterfall_automatic",value);
+    sprintf(value,"%f",volume);
+    setProperty("volume",value);
+    sprintf(value,"%f",mic_gain);
+    setProperty("mic_gain",value);
+    sprintf(value,"%d",orion_mic);
+    setProperty("orion_mic",value);
+    sprintf(value,"%d",nr);
+    setProperty("nr",value);
+    sprintf(value,"%d",nb);
+    setProperty("nb",value);
+    sprintf(value,"%d",anf);
+    setProperty("anf",value);
+    sprintf(value,"%d",snb);
+    setProperty("snb",value);
+    sprintf(value,"%d",agc);
+    setProperty("agc",value);
+    sprintf(value,"%f",agc_gain);
+    setProperty("agc_gain",value);
+    sprintf(value,"%d",step);
+    setProperty("step",value);
+    sprintf(value,"%d",byte_swap);
+    setProperty("byte_swap",value);
+    sprintf(value,"%d",cw_keys_reversed);
+    setProperty("cw_keys_reversed",value);
+    sprintf(value,"%d",cw_keyer_speed);
+    setProperty("cw_keyer_speed",value);
+    sprintf(value,"%d",cw_keyer_mode);
+    setProperty("cw_keyer_mode",value);
+    sprintf(value,"%d",cw_keyer_weight);
+    setProperty("cw_keyer_weight",value);
+    sprintf(value,"%d",cw_keyer_spacing);
+    setProperty("cw_keyer_spacing",value);
+    sprintf(value,"%d",cw_keyer_internal);
+    setProperty("cw_keyer_internal",value);
+    sprintf(value,"%d",cw_keyer_sidetone_volume);
+    setProperty("cw_keyer_sidetone_volume",value);
+    sprintf(value,"%d",cw_keyer_ptt_delay);
+    setProperty("cw_keyer_ptt_delay",value);
+    sprintf(value,"%d",cw_keyer_hang_time);
+    setProperty("cw_keyer_hang_time",value);
+    sprintf(value,"%d",cw_keyer_sidetone_frequency);
+    setProperty("cw_keyer_sidetone_frequency",value);
+    sprintf(value,"%d",cw_breakin);
+    setProperty("cw_breakin",value);
+
+    bandSaveState();
+}
diff --git a/radio.h b/radio.h
new file mode 100644 (file)
index 0000000..28ef822
--- /dev/null
+++ b/radio.h
@@ -0,0 +1,80 @@
+
+#define MIC_IN 0x00
+#define LINE_IN 0x01
+#define MIC_BOOST 0x02
+#define ORION_MIC_PTT_ENABLED 0x00
+#define ORION_MIC_PTT_DISABLED 0x04
+#define ORION_MIC_PTT_RING_BIAS_TIP 0x00
+#define ORION_MIC_PTT_TIP_BIAS_RING 0x08
+#define ORION_MIC_BIAS_DISABLED 0x00
+#define ORION_MIC_BIAS_ENABLED 0x10
+
+extern char property_path[];
+
+#define NONE 0
+
+#define ALEX 1
+#define APOLLO 2
+
+#define PA_DISABLED 0
+#define PA_ENABLED 1
+
+#define APOLLO_TUNER 1
+
+#define KEYER_STRAIGHT 0
+#define KEYER_MODE_A 1
+#define KEYER_MODE_B 2
+
+extern int sample_rate;
+extern int filter_board;
+extern int pa;
+extern int apollo_tuner;
+
+extern int panadapter_high;
+extern int panadapter_low;
+
+extern int waterfall_high;
+extern int waterfall_low;
+extern int waterfall_automatic;
+
+extern double volume;
+extern double mic_gain;
+extern int agc;
+extern double agc_gain;
+
+extern int nr;
+extern int nb;
+extern int anf;
+extern int snb;
+
+extern int cwPitch;
+
+extern int orion_mic;
+
+extern int tune_drive;
+extern int drive;
+
+int receivers;
+int adc[2];
+
+int locked;
+
+extern int step;
+
+extern int byte_swap;
+
+extern int cw_keys_reversed;
+extern int cw_keyer_speed;
+extern int cw_keyer_mode;
+extern int cw_keyer_weight;
+extern int cw_keyer_spacing;
+extern int cw_keyer_internal;
+extern int cw_keyer_sidetone_volume;
+extern int cw_keyer_ptt_delay;
+extern int cw_keyer_hang_time;
+extern int cw_keyer_sidetone_frequency;
+extern int cw_breakin;
+
+extern void radioRestoreState();
+extern void radioSaveState();
+
diff --git a/rotary_encoder.c b/rotary_encoder.c
new file mode 100644 (file)
index 0000000..9e70db4
--- /dev/null
@@ -0,0 +1,298 @@
+#include <stdio.h>
+#include <pigpio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sched.h>
+#include <pthread.h>
+#include <wiringPi.h>
+
+#include "rotary_encoder.h"
+#include "main.h"
+
+#define SYSFS_GPIO_DIR  "/sys/class/gpio"
+
+#define RASPBERRYPI_VFO_ENCODER_A 14
+#define RASPBERRYPI_VFO_ENCODER_B 15
+
+#define ODROID_VFO_ENCODER_A 108
+#define ODROID_VFO_ENCODER_B 97
+#define ODROID_VFO_ENCODER_A_PIN 23
+#define ODROID_VFO_ENCODER_B_PIN 24
+
+#define AF_ENCODER_A 20
+#define AF_ENCODER_B 26
+#define AF_FUNCTION 21
+
+#define RF_ENCODER_A 16
+#define RF_ENCODER_B 19
+#define RF_FUNCTION 13
+
+#define FUNCTION 12
+#define BAND 6
+
+static int VFO_ENCODER_A=14;
+static int VFO_ENCODER_B=15;
+
+static int VFO_ENCODER_A_PIN=0;
+static int VFO_ENCODER_B_PIN=0;
+
+static volatile int vfoEncoderPos;
+static volatile int afEncoderPos;
+static volatile int afFunction;
+static volatile int rfEncoderPos;
+static volatile int rfFunction;
+static volatile int function;
+static volatile int band;
+
+static void afFunctionAlert(int gpio, int level, uint32_t tick) {
+    afFunction=(level==0);
+}
+
+static void rfFunctionAlert(int gpio, int level, uint32_t tick) {
+    rfFunction=(level==0);
+}
+
+static void functionAlert(int gpio, int level, uint32_t tick) {
+    function=(level==0);
+}
+
+static void bandAlert(int gpio, int level, uint32_t tick) {
+    band=(level==0);
+}
+
+static void vfoEncoderPulse(int gpio, int level, unsigned int tick) {
+   static int levA=0, levB=0, lastGpio = -1;
+
+//fprintf(stderr,"vfoEncoderPulse:%d=%d\n",gpio,level);
+   if (gpio == VFO_ENCODER_A) levA = level; else levB = level;
+
+   if (gpio != lastGpio) /* debounce */
+   {
+      lastGpio = gpio;
+
+      if ((gpio == VFO_ENCODER_A) && (level == 0))
+      {
+         if (!levB) ++vfoEncoderPos;
+      }
+      else if ((gpio == VFO_ENCODER_B) && (level == 1))
+      {
+         if (levA) --vfoEncoderPos;
+      }
+   }
+}
+
+static void afEncoderPulse(int gpio, int level, uint32_t tick)
+{
+   static int levA=0, levB=0, lastGpio = -1;
+
+   if (gpio == AF_ENCODER_A) levA = level; else levB = level;
+
+   if (gpio != lastGpio) /* debounce */
+   {
+      lastGpio = gpio;
+
+      if ((gpio == AF_ENCODER_A) && (level == 0))
+      {
+         if (!levB) ++afEncoderPos;
+      }
+      else if ((gpio == AF_ENCODER_B) && (level == 1))
+      {
+         if (levA) --afEncoderPos;
+      }
+   }
+}
+
+static void rfEncoderPulse(int gpio, int level, uint32_t tick)
+{
+   static int levA=0, levB=0, lastGpio = -1;
+
+   if (gpio == RF_ENCODER_A) levA = level; else levB = level;
+
+   if (gpio != lastGpio) /* debounce */
+   {
+      lastGpio = gpio;
+
+      if ((gpio == RF_ENCODER_A) && (level == 0))
+      {
+         if (!levB) ++rfEncoderPos;
+      }
+      else if ((gpio == RF_ENCODER_B) && (level == 1))
+      {
+         if (levA) --rfEncoderPos;
+      }
+   }
+}
+
+void interruptB(void) {
+   vfoEncoderPulse(VFO_ENCODER_B,digitalRead(VFO_ENCODER_B_PIN),0);
+}
+
+void interruptA(void) {
+   vfoEncoderPulse(VFO_ENCODER_A,digitalRead(VFO_ENCODER_A_PIN),0);
+}
+
+int encoder_init() {
+
+    if(strcmp(unameData.nodename,"raspberrypi")==0) {
+
+    VFO_ENCODER_A=RASPBERRYPI_VFO_ENCODER_A;
+    VFO_ENCODER_B=RASPBERRYPI_VFO_ENCODER_B;
+    VFO_ENCODER_A_PIN=0;
+    VFO_ENCODER_B_PIN=0;
+
+    fprintf(stderr,"encoder_init: VFO_ENCODER_A=%d VFO_ENCODER_B=%d\n",VFO_ENCODER_A,VFO_ENCODER_B);
+
+    if(gpioInitialise()<0) {
+        fprintf(stderr,"Cannot initialize GPIO\n");
+        return -1;
+    }
+
+   gpioSetMode(VFO_ENCODER_A, PI_INPUT);
+   gpioSetMode(VFO_ENCODER_B, PI_INPUT);
+   gpioSetPullUpDown(VFO_ENCODER_A, PI_PUD_UP);
+   gpioSetPullUpDown(VFO_ENCODER_B, PI_PUD_UP);
+   gpioSetAlertFunc(VFO_ENCODER_A, vfoEncoderPulse);
+   gpioSetAlertFunc(VFO_ENCODER_B, vfoEncoderPulse);
+   vfoEncoderPos=0;
+
+
+   gpioSetMode(AF_FUNCTION, PI_INPUT);
+   gpioSetPullUpDown(AF_FUNCTION,PI_PUD_UP);
+   gpioSetAlertFunc(AF_FUNCTION, afFunctionAlert);
+   afFunction=0;
+
+   gpioSetMode(AF_ENCODER_A, PI_INPUT);
+   gpioSetMode(AF_ENCODER_B, PI_INPUT);
+   gpioSetPullUpDown(AF_ENCODER_A, PI_PUD_OFF);
+   gpioSetPullUpDown(AF_ENCODER_B, PI_PUD_OFF);
+   gpioSetAlertFunc(AF_ENCODER_A, afEncoderPulse);
+   gpioSetAlertFunc(AF_ENCODER_B, afEncoderPulse);
+   afEncoderPos=0;
+
+   gpioSetMode(RF_FUNCTION, PI_INPUT);
+   gpioSetPullUpDown(RF_FUNCTION,PI_PUD_UP);
+   gpioSetAlertFunc(RF_FUNCTION, rfFunctionAlert);
+   rfFunction=0;
+
+   gpioSetMode(RF_ENCODER_A, PI_INPUT);
+   gpioSetMode(RF_ENCODER_B, PI_INPUT);
+   gpioSetPullUpDown(RF_ENCODER_A, PI_PUD_OFF);
+   gpioSetPullUpDown(RF_ENCODER_B, PI_PUD_OFF);
+   gpioSetAlertFunc(RF_ENCODER_A, rfEncoderPulse);
+   gpioSetAlertFunc(RF_ENCODER_B, rfEncoderPulse);
+   rfEncoderPos=0;
+
+   gpioSetMode(FUNCTION, PI_INPUT);
+   gpioSetPullUpDown(FUNCTION,PI_PUD_UP);
+   gpioSetAlertFunc(FUNCTION, functionAlert);
+
+   gpioSetMode(BAND, PI_INPUT);
+   gpioSetPullUpDown(BAND,PI_PUD_UP);
+   gpioSetAlertFunc(BAND, bandAlert);
+
+   } else if(strcmp(unameData.nodename,"odroid")==0) {
+
+    VFO_ENCODER_A=ODROID_VFO_ENCODER_A;
+    VFO_ENCODER_B=ODROID_VFO_ENCODER_B;
+    VFO_ENCODER_A_PIN=ODROID_VFO_ENCODER_A_PIN;
+    VFO_ENCODER_B_PIN=ODROID_VFO_ENCODER_B_PIN;
+
+    fprintf(stderr,"encoder_init: VFO_ENCODER_A=%d VFO_ENCODER_B=%d\n",VFO_ENCODER_A,VFO_ENCODER_B);
+
+  if (wiringPiSetup () < 0) {
+      printf ("Unable to setup wiringPi: %s\n", strerror (errno));
+      return 1;
+  }
+
+  FILE *fp;
+
+  fp = popen("echo 97 > /sys/class/gpio/export\n", "r");
+  pclose(fp);
+  fp = popen("echo \"in\" > /sys/class/gpio/gpio97/direction\n", "r");
+  pclose(fp);
+  fp = popen("chmod 0666 /sys/class/gpio/gpio97/value\n", "r");
+  pclose(fp);
+
+  fp = popen("echo 108 > /sys/class/gpio/export\n", "r");
+  pclose(fp);
+  fp = popen("echo \"in\" > /sys/class/gpio/gpio108/direction\n", "r");
+  pclose(fp);
+  fp = popen("chmod 0666 /sys/class/gpio/gpio108/value\n", "r");
+  pclose(fp);
+
+  if ( wiringPiISR (24, INT_EDGE_BOTH, &interruptB) < 0 ) {
+      printf ("Unable to setup ISR: %s\n", strerror (errno));
+      return 1;
+  }
+
+  if ( wiringPiISR (23, INT_EDGE_BOTH, &interruptA) < 0 ) {
+      printf ("Unable to setup ISR: %s\n", strerror (errno));
+      return 1;
+  }
+  } else {
+     fprintf(stderr,"Unknown nodename: %s. Rotary Encoder not enabled.\n",unameData.nodename);
+     return 1;
+  }
+
+
+   return 0;
+}
+
+void encoder_close() {
+  if(strcmp(unameData.nodename,"odroid")==0) {
+    FILE *fp;
+    fp = popen("echo 97 > /sys/class/gpio/unexport\n", "r");
+    pclose(fp);
+    fp = popen("echo 108 > /sys/class/gpio/unexport\n", "r");
+    pclose(fp);
+  }
+}
+
+int vfo_encoder_get_pos() {
+    int pos=vfoEncoderPos;
+
+  if(strcmp(unameData.nodename,"raspberrypi")==0) {
+    if(pos<0 && pos>-12) {
+        pos=0;
+    } else if(pos>0 && pos<12) {
+        pos=0;
+    }
+    pos=pos/12;
+    vfoEncoderPos=vfoEncoderPos-(pos*12);
+  } else if(strcmp(unameData.nodename,"odroid")==0) {
+    vfoEncoderPos=0;
+  }
+  return pos;
+}
+
+int af_encoder_get_pos() {
+    int pos=afEncoderPos;
+    afEncoderPos=0;
+    return pos;
+}
+
+int af_function_get_state() {
+    return afFunction;
+}
+
+int rf_encoder_get_pos() {
+    int pos=rfEncoderPos;
+    rfEncoderPos=0;
+    return pos;
+}
+
+int rf_function_get_state() {
+    return rfFunction;
+}
+
+int function_get_state() {
+    return function;
+}
+
+int band_get_state() {
+    return band;
+}
diff --git a/rotary_encoder.h b/rotary_encoder.h
new file mode 100644 (file)
index 0000000..604feb1
--- /dev/null
@@ -0,0 +1,6 @@
+int encoder_init();
+void encoder_close();
+int vfo_encoder_get_pos();
+int af_encoder_get_pos();
+int af_function_get_state();
+
diff --git a/splash.c b/splash.c
new file mode 100644 (file)
index 0000000..53c7909
--- /dev/null
+++ b/splash.c
@@ -0,0 +1,23 @@
+#include <gtk/gtk.h>
+
+GtkWidget *splash_window;
+
+/* Close the splash screen */
+void splash_close()
+{
+  gtk_widget_destroy(splash_window);
+}
+
+
+void splash_show(char* image_name,int time,int width,int height)
+{
+  GtkWidget  *image;
+  splash_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_size_request(splash_window, width, height);
+  gtk_window_set_decorated(GTK_WINDOW(splash_window), FALSE);
+  gtk_window_set_position(GTK_WINDOW(splash_window),GTK_WIN_POS_CENTER_ALWAYS);
+  gtk_window_set_resizable(GTK_WINDOW(splash_window), FALSE);
+  image=gtk_image_new_from_file(image_name);
+  gtk_container_add(GTK_CONTAINER(splash_window), image);
+  gtk_widget_show_all (splash_window);
+}
diff --git a/splash.h b/splash.h
new file mode 100644 (file)
index 0000000..5adb9ef
--- /dev/null
+++ b/splash.h
@@ -0,0 +1,5 @@
+
+extern GtkWidget* splash_window;
+
+void splash_close(void);
+void splash_show(char* image_name,int time,int width,int height);
diff --git a/splash.png b/splash.png
new file mode 100644 (file)
index 0000000..4b249eb
Binary files /dev/null and b/splash.png differ
diff --git a/start_pihpsdr.sh b/start_pihpsdr.sh
new file mode 100755 (executable)
index 0000000..98af331
--- /dev/null
@@ -0,0 +1,2 @@
+cd ~/pihpsdr
+sudo ~/pihpsdr/pihpsdr
diff --git a/toolbar.c b/toolbar.c
new file mode 100644 (file)
index 0000000..048ad04
--- /dev/null
+++ b/toolbar.c
@@ -0,0 +1,1007 @@
+#include <gtk/gtk.h>
+#include <semaphore.h>
+#include <stdio.h>
+
+#include "toolbar.h"
+#include "mode.h"
+#include "filter.h"
+#include "bandstack.h"
+#include "band.h"
+#include "discovered.h"
+#include "new_protocol.h"
+#include "rotary_encoder.h"
+#include "vfo.h"
+#include "alex.h"
+#include "agc.h"
+#include "channel.h"
+#include "wdsp.h"
+#include "radio.h"
+#include "property.h"
+
+static int width;
+static int height;
+
+static int column=0;
+
+static GtkWidget *parent_window;
+static GtkWidget *toolbar;
+static GtkWidget *toolbar_bottom;
+static GtkWidget *toolbar_top;
+
+static GtkWidget *last_band;
+static GtkWidget *last_mode;
+static GtkWidget *last_filter;
+
+static GtkWidget *af_gain_label;
+static GtkWidget *audio_scale;
+static GtkWidget *agc_gain_label;
+static GtkWidget *agc_scale;
+static GtkWidget *mic_gain_label;
+static GtkWidget *mic_scale;
+static GtkWidget *drive_scale;
+static GtkWidget *tune_scale;
+
+static GdkRGBA white;
+static GdkRGBA gray;
+
+static void band_select_cb(GtkWidget *widget, gpointer data) {
+  GtkWidget *label;
+  int b=(int)data;
+  BANDSTACK_ENTRY *entry;
+  if(b==band_get_current()) {
+    entry=bandstack_entry_next();
+  } else {
+    BAND* band=band_set_current(b);
+    entry=bandstack_entry_get_current();
+    gtk_widget_override_background_color(last_band, GTK_STATE_NORMAL, &white);
+    last_band=widget;
+    gtk_widget_override_background_color(last_band, GTK_STATE_NORMAL, &gray);
+  }
+  setFrequency(entry->frequencyA);
+  setMode(entry->mode);
+  FILTER* band_filters=filters[entry->mode];
+  FILTER* band_filter=&band_filters[entry->filter];
+  setFilter(band_filter->low,band_filter->high);
+
+  BAND *band=band_get_current_band();
+  set_alex_rx_antenna(band->alexRxAntenna);
+  set_alex_tx_antenna(band->alexTxAntenna);
+  set_alex_attenuation(band->alexAttenuation);
+  vfo_update(NULL);
+}
+
+static void band_cb(GtkWidget *widget, gpointer data) {
+  GtkWidget *dialog=gtk_dialog_new_with_buttons("Band",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+  GtkWidget *grid=gtk_grid_new();
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+  GtkWidget *b;
+  int i;
+  for(i=0;i<BANDS;i++) {
+    BAND* band=band_get_band(i);
+    GtkWidget *b=gtk_button_new_with_label(band->title);
+    gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &white);
+    gtk_widget_override_font(b, pango_font_description_from_string("Arial 20"));
+    if(i==band_get_current()) {
+      gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &gray);
+      last_band=b;
+    }
+    gtk_widget_show(b);
+    gtk_grid_attach(GTK_GRID(grid),b,i%5,i/5,1,1);
+    g_signal_connect(b,"clicked",G_CALLBACK(band_select_cb),(gpointer *)i);
+  }
+  
+  gtk_container_add(GTK_CONTAINER(content),grid);
+
+  GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK);
+  gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20"));
+  gtk_widget_show_all(dialog);
+
+  g_signal_connect_swapped (dialog,
+                           "response",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
+
+  int result=gtk_dialog_run(GTK_DIALOG(dialog));
+  
+}
+
+static void mode_select_cb(GtkWidget *widget, gpointer data) {
+  int m=(int)data;
+  BANDSTACK_ENTRY *entry;
+  entry=bandstack_entry_get_current();
+  entry->mode=m;
+  setMode(entry->mode);
+  FILTER* band_filters=filters[entry->mode];
+  FILTER* band_filter=&band_filters[entry->filter];
+  setFilter(band_filter->low,band_filter->high);
+  gtk_widget_override_background_color(last_mode, GTK_STATE_NORMAL, &white);
+  last_mode=widget;
+  gtk_widget_override_background_color(last_mode, GTK_STATE_NORMAL, &gray);
+  vfo_update(NULL);
+}
+
+static void mode_cb(GtkWidget *widget, gpointer data) {
+  BANDSTACK_ENTRY *entry=bandstack_entry_get_current();
+  GtkWidget *dialog=gtk_dialog_new_with_buttons("Mode",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+  GtkWidget *grid=gtk_grid_new();
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+
+  GtkWidget *b;
+  int i;
+  for(i=0;i<MODES;i++) {
+    GtkWidget *b=gtk_button_new_with_label(mode_string[i]);
+    if(i==entry->mode) {
+      gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &gray);
+      last_mode=b;
+    } else {
+      gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &white);
+    }
+    gtk_widget_override_font(b, pango_font_description_from_string("Arial 20"));
+    gtk_widget_show(b);
+    gtk_grid_attach(GTK_GRID(grid),b,i%5,i/5,1,1);
+    g_signal_connect(b,"pressed",G_CALLBACK(mode_select_cb),(gpointer *)i);
+  }
+  gtk_container_add(GTK_CONTAINER(content),grid);
+  GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK);
+  gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20"));
+  gtk_widget_show_all(dialog);
+
+  g_signal_connect_swapped (dialog,
+                           "response",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
+
+  int result=gtk_dialog_run(GTK_DIALOG(dialog));
+
+}
+
+static void filter_select_cb(GtkWidget *widget, gpointer data) {
+  int f=(int)data;
+  BANDSTACK_ENTRY *entry;
+  entry=bandstack_entry_get_current();
+  entry->filter=f;
+  FILTER* band_filters=filters[entry->mode];
+  FILTER* band_filter=&band_filters[entry->filter];
+  setFilter(band_filter->low,band_filter->high);
+  gtk_widget_override_background_color(last_filter, GTK_STATE_NORMAL, &white);
+  last_filter=widget;
+  gtk_widget_override_background_color(last_filter, GTK_STATE_NORMAL, &gray);
+  vfo_update(NULL);
+}
+
+static void filter_cb(GtkWidget *widget, gpointer data) {
+  BANDSTACK_ENTRY *entry=bandstack_entry_get_current();
+  FILTER* band_filters=filters[entry->mode];
+  GtkWidget *dialog=gtk_dialog_new_with_buttons("Mode",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+  GtkWidget *grid=gtk_grid_new();
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+
+  GtkWidget *b;
+  int i;
+  for(i=0;i<FILTERS;i++) {
+    FILTER* band_filter=&band_filters[i];
+    GtkWidget *b=gtk_button_new_with_label(band_filters[i].title);
+    gtk_widget_override_font(b, pango_font_description_from_string("Arial 20"));
+    if(i==entry->filter) {
+      gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &gray);
+      last_filter=b;
+    } else {
+      gtk_widget_override_background_color(b, GTK_STATE_NORMAL, &white);
+    }
+    gtk_widget_show(b);
+    gtk_grid_attach(GTK_GRID(grid),b,i%5,i/5,1,1);
+    g_signal_connect(b,"pressed",G_CALLBACK(filter_select_cb),(gpointer *)i);
+  }
+  gtk_container_add(GTK_CONTAINER(content),grid);
+  GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK);
+  gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 20"));
+  gtk_widget_show_all(dialog);
+
+  g_signal_connect_swapped (dialog,
+                           "response",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
+
+  int result=gtk_dialog_run(GTK_DIALOG(dialog));
+
+}
+
+static void agc_select_cb(GtkWidget *widget, gpointer data) {
+  agc=(int)data;
+  SetRXAAGCMode(CHANNEL_RX0, agc);
+}
+
+static void agcgain_value_changed_cb(GtkWidget *widget, gpointer data) {
+  agc_gain=gtk_range_get_value(GTK_RANGE(widget));
+  SetRXAAGCTop(CHANNEL_RX0, agc_gain);
+}
+
+void set_agc_gain(double value) {
+    agc_gain=value;
+    gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain);
+    SetRXAAGCTop(CHANNEL_RX0, agc_gain);
+}
+
+static void afgain_value_changed_cb(GtkWidget *widget, gpointer data) {
+    volume=gtk_range_get_value(GTK_RANGE(widget));
+}
+
+void set_af_gain(double value) {
+    volume=value;
+    gtk_range_set_value (GTK_RANGE(audio_scale),volume);
+}
+
+static void micgain_value_changed_cb(GtkWidget *widget, gpointer data) {
+    mic_gain=gtk_range_get_value(GTK_RANGE(widget));
+}
+
+static void nr_cb(GtkWidget *widget, gpointer data) {
+  nr=nr==0?1:0;
+  SetRXAANRRun(CHANNEL_RX0, nr);
+}
+
+static void nb_cb(GtkWidget *widget, gpointer data) {
+  nb=nb==0?1:0;
+  SetRXAEMNRRun(CHANNEL_RX0, nb);
+}
+
+static void anf_cb(GtkWidget *widget, gpointer data) {
+  anf=anf==0?1:0;
+  SetRXAANFRun(CHANNEL_RX0, anf);
+}
+
+static void snb_cb(GtkWidget *widget, gpointer data) {
+  snb=snb==0?1:0;
+  SetRXASNBARun(CHANNEL_RX0, snb);
+}
+
+static void linein_cb(GtkWidget *widget, gpointer data) {
+  if((orion_mic&0x01)==LINE_IN) {
+      orion_mic=orion_mic&0xFE;
+  } else {
+      orion_mic=orion_mic|LINE_IN;
+  }
+}
+
+static void micboost_cb(GtkWidget *widget, gpointer data) {
+  if((orion_mic&0x02)==MIC_BOOST) {
+      orion_mic=orion_mic&0xFD;
+  } else {
+      orion_mic=orion_mic|MIC_BOOST;
+  }
+}
+
+static void byteswap_cb(GtkWidget *widget, gpointer data) {
+  byte_swap=byte_swap?0:1;
+}
+
+static void ptt_cb(GtkWidget *widget, gpointer data) {
+  if((orion_mic&0x04)==ORION_MIC_PTT_ENABLED) {
+      orion_mic=orion_mic|ORION_MIC_PTT_DISABLED;
+  } else {
+      orion_mic=orion_mic&0xFB;
+  }
+}
+
+static void ptt_ring_cb(GtkWidget *widget, gpointer data) {
+  if((orion_mic&0x08)==ORION_MIC_PTT_RING_BIAS_TIP) {
+      orion_mic=orion_mic|ORION_MIC_PTT_TIP_BIAS_RING;
+  } else {
+      orion_mic=orion_mic&0xF7;
+  }
+}
+
+static void bias_cb(GtkWidget *widget, gpointer data) {
+  if((orion_mic&0x10)==ORION_MIC_BIAS_DISABLED) {
+      orion_mic=orion_mic|ORION_MIC_BIAS_ENABLED;
+  } else {
+      orion_mic=orion_mic&0xEF;
+  }
+}
+
+static void audio_cb(GtkWidget *widget, gpointer data) {
+  GtkWidget *dialog=gtk_dialog_new_with_buttons("Audio",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+  GtkWidget *grid=gtk_grid_new();
+
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+
+  GtkWidget *b_off=gtk_radio_button_new_with_label(NULL,"Off"); 
+  gtk_widget_override_font(b_off, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_off), agc==AGC_OFF);
+  gtk_widget_show(b_off);
+  gtk_grid_attach(GTK_GRID(grid),b_off,0,0,2,1);
+  g_signal_connect(b_off,"pressed",G_CALLBACK(agc_select_cb),(gpointer *)AGC_OFF);
+
+  GtkWidget *b_long=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(b_off),"Long"); 
+  gtk_widget_override_font(b_long, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_long), agc==AGC_LONG);
+  gtk_widget_show(b_long);
+  gtk_grid_attach(GTK_GRID(grid),b_long,0,1,2,1);
+  g_signal_connect(b_long,"pressed",G_CALLBACK(agc_select_cb),(gpointer *)AGC_LONG);
+
+  GtkWidget *b_slow=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(b_long),"Slow"); 
+  gtk_widget_override_font(b_slow, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_slow), agc==AGC_SLOW);
+  gtk_widget_show(b_slow);
+  gtk_grid_attach(GTK_GRID(grid),b_slow,0,2,2,1);
+  g_signal_connect(b_slow,"pressed",G_CALLBACK(agc_select_cb),(gpointer *)AGC_SLOW);
+
+  GtkWidget *b_medium=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(b_slow),"Medium"); 
+  gtk_widget_override_font(b_medium, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_medium), agc==AGC_MEDIUM);
+  gtk_widget_show(b_medium);
+  gtk_grid_attach(GTK_GRID(grid),b_medium,0,3,2,1);
+  g_signal_connect(b_medium,"pressed",G_CALLBACK(agc_select_cb),(gpointer *)AGC_MEDIUM);
+
+  GtkWidget *b_fast=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(b_medium),"Fast"); 
+  gtk_widget_override_font(b_fast, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_fast), agc==AGC_FAST);
+  gtk_widget_show(b_fast);
+  gtk_grid_attach(GTK_GRID(grid),b_fast,0,4,2,1);
+  g_signal_connect(b_fast,"pressed",G_CALLBACK(agc_select_cb),(gpointer *)AGC_FAST);
+
+
+  GtkWidget *b_nr=gtk_check_button_new_with_label("NR");
+  gtk_widget_override_font(b_nr, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_nr), nr==1);
+  gtk_widget_show(b_nr);
+  gtk_grid_attach(GTK_GRID(grid),b_nr,2,0,2,1);
+  g_signal_connect(b_nr,"toggled",G_CALLBACK(nr_cb),NULL);
+
+  GtkWidget *b_nb=gtk_check_button_new_with_label("NB");
+  gtk_widget_override_font(b_nb, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_nb), nb==1);
+  gtk_widget_show(b_nb);
+  gtk_grid_attach(GTK_GRID(grid),b_nb,2,1,2,1);
+  g_signal_connect(b_nb,"toggled",G_CALLBACK(nb_cb),NULL);
+
+  GtkWidget *b_anf=gtk_check_button_new_with_label("ANF");
+  gtk_widget_override_font(b_anf, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_anf), anf==1);
+  gtk_widget_show(b_anf);
+  gtk_grid_attach(GTK_GRID(grid),b_anf,2,2,2,1);
+  g_signal_connect(b_anf,"toggled",G_CALLBACK(anf_cb),NULL);
+
+  GtkWidget *b_snb=gtk_check_button_new_with_label("SNB");
+  gtk_widget_override_font(b_snb, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b_snb), snb==1);
+  gtk_widget_show(b_snb);
+  gtk_grid_attach(GTK_GRID(grid),b_snb,2,3,2,1);
+  g_signal_connect(b_snb,"toggled",G_CALLBACK(snb_cb),NULL);
+
+  gtk_container_add(GTK_CONTAINER(content),grid);
+  GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK);
+  gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show_all(dialog);
+
+  g_signal_connect_swapped (dialog,
+                           "response",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
+
+  int result=gtk_dialog_run(GTK_DIALOG(dialog));
+
+}
+
+void set_drive(double value) {
+  setDrive(value);
+  gtk_range_set_value (GTK_RANGE(drive_scale),value);
+}
+
+static void drive_value_changed_cb(GtkWidget *widget, gpointer data) {
+  setDrive(gtk_range_get_value(GTK_RANGE(widget)));
+}
+
+void set_tune(double value) {
+  setTuneDrive(value);
+  gtk_range_set_value (GTK_RANGE(tune_scale),value);
+}
+
+static void tune_value_changed_cb(GtkWidget *widget, gpointer data) {
+  setTuneDrive(gtk_range_get_value(GTK_RANGE(widget)));
+}
+
+static void yes_cb(GtkWidget *widget, gpointer data) {
+  encoder_close();
+  _exit(0);
+}
+
+static void halt_cb(GtkWidget *widget, gpointer data) {
+  encoder_close();
+  system("shutdown -h -P now");
+  _exit(0);
+}
+
+static void exit_cb(GtkWidget *widget, gpointer data) {
+
+  radioSaveState();
+  saveProperties(property_path);
+
+  GtkWidget *dialog=gtk_dialog_new_with_buttons("Audio",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+  GtkWidget *grid=gtk_grid_new();
+
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+
+  GtkWidget *label=gtk_label_new("Exit?");
+  gtk_widget_override_font(label, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show(label);
+  gtk_grid_attach(GTK_GRID(grid),label,1,0,1,1);
+
+  GtkWidget *b_yes=gtk_button_new_with_label("Yes (to CLI)");
+  gtk_widget_override_font(b_yes, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show(b_yes);
+  gtk_grid_attach(GTK_GRID(grid),b_yes,0,1,1,1);
+  g_signal_connect(b_yes,"pressed",G_CALLBACK(yes_cb),NULL);
+
+  GtkWidget *b_halt=gtk_button_new_with_label("Halt System");
+  gtk_widget_override_font(b_halt, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show(b_halt);
+  gtk_grid_attach(GTK_GRID(grid),b_halt,2,1,1,1);
+  g_signal_connect(b_halt,"pressed",G_CALLBACK(halt_cb),NULL);
+
+  gtk_container_add(GTK_CONTAINER(content),grid);
+  GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Cancel",GTK_RESPONSE_OK);
+  gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show_all(dialog);
+
+  g_signal_connect_swapped (dialog,
+                           "response",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
+
+  int result=gtk_dialog_run(GTK_DIALOG(dialog));
+
+}
+
+static void apollo_cb(GtkWidget *widget, gpointer data);
+
+static void alex_cb(GtkWidget *widget, gpointer data) {
+fprintf(stderr,"alex_cb\n");
+  if(filter_board==ALEX) {
+fprintf(stderr,"alex_cb: was ALEX setting NONE\n");
+    filter_board=NONE;
+  } else if(filter_board==NONE) {
+fprintf(stderr,"alex_cb: was NONE setting ALEX\n");
+    filter_board=ALEX;
+  } else if(filter_board==APOLLO) {
+fprintf(stderr,"alex_cb: was APOLLO setting ALEX\n");
+    GtkWidget *w=(GtkWidget *)data;
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE);
+    filter_board=ALEX;
+  }
+  filter_board_changed();
+}
+
+static void apollo_cb(GtkWidget *widget, gpointer data) {
+fprintf(stderr,"apollo_cb\n");
+  if(filter_board==APOLLO) {
+fprintf(stderr,"apollo_cb: was APOLLO setting NONE\n");
+    filter_board=NONE;
+  } else if(filter_board==NONE) {
+fprintf(stderr,"apollo_cb: was NONE setting APOLLO\n");
+    filter_board=APOLLO;
+  } else if(filter_board==ALEX) {
+fprintf(stderr,"apollo_cb: was ALEX setting APOLLO\n");
+    GtkWidget *w=(GtkWidget *)data;
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), FALSE);
+    filter_board=APOLLO;
+  }
+  filter_board_changed();
+}
+
+static void apollo_tuner_cb(GtkWidget *widget, gpointer data) {
+  apollo_tuner=apollo_tuner==1?0:1;
+  tuner_changed();
+}
+
+static void pa_cb(GtkWidget *widget, gpointer data) {
+  pa=pa==1?0:1;
+  pa_changed();
+}
+
+static void cw_keyer_internal_cb(GtkWidget *widget, gpointer data) {
+  cw_keyer_internal=cw_keyer_internal==1?0:1;
+  cw_changed();
+}
+
+static void cw_keyer_speed_value_changed_cb(GtkWidget *widget, gpointer data) {
+  cw_keyer_speed=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+  cw_changed();
+}
+
+static void cw_breakin_cb(GtkWidget *widget, gpointer data) {
+  cw_breakin=cw_breakin==1?0:1;
+  cw_changed();
+}
+
+static void cw_keyer_hang_time_value_changed_cb(GtkWidget *widget, gpointer data) {
+  cw_keyer_hang_time=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget));
+  cw_changed();
+}
+
+static void cw_keys_reversed_cb(GtkWidget *widget, gpointer data) {
+  cw_keys_reversed=cw_keys_reversed==1?0:1;
+  cw_changed();
+}
+
+static void cw_keyer_mode_cb(GtkWidget *widget, gpointer data) {
+  cw_keyer_mode=(int)data;
+  cw_changed();
+}
+
+static void config_cb(GtkWidget *widget, gpointer data) {
+  GtkWidget *dialog=gtk_dialog_new_with_buttons("Audio",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+  GtkWidget *grid=gtk_grid_new();
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+
+  GtkWidget *linein_b=gtk_check_button_new_with_label("Line In");
+  gtk_widget_override_font(linein_b, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (linein_b), (orion_mic&0x01)==LINE_IN);
+  gtk_widget_show(linein_b);
+  gtk_grid_attach(GTK_GRID(grid),linein_b,0,0,1,1);
+  g_signal_connect(linein_b,"toggled",G_CALLBACK(linein_cb),NULL);
+
+  GtkWidget *micboost_b=gtk_check_button_new_with_label("Boost");
+  gtk_widget_override_font(micboost_b, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (micboost_b), (orion_mic&0x02)==MIC_BOOST);
+  gtk_widget_show(micboost_b);
+  gtk_grid_attach(GTK_GRID(grid),micboost_b,0,1,1,1);
+  g_signal_connect(micboost_b,"toggled",G_CALLBACK(micboost_cb),NULL);
+
+  GtkWidget *byteswap_b=gtk_check_button_new_with_label("Byte swap");
+  gtk_widget_override_font(byteswap_b, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (byteswap_b), byte_swap);
+  gtk_widget_show(byteswap_b);
+  gtk_grid_attach(GTK_GRID(grid),byteswap_b,0,2,1,1);
+  g_signal_connect(byteswap_b,"toggled",G_CALLBACK(byteswap_cb),NULL);
+
+  DISCOVERED* d=&discovered[selected_device];
+  if(d->device==NEW_DEVICE_ORION) {
+    GtkWidget *ptt_b=gtk_check_button_new_with_label("PTT Enabled");
+    gtk_widget_override_font(ptt_b, pango_font_description_from_string("Arial 18"));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_b), (orion_mic&0x04)==ORION_MIC_PTT_ENABLED);
+    gtk_widget_show(ptt_b);
+    gtk_grid_attach(GTK_GRID(grid),ptt_b,0,3,1,1);
+    g_signal_connect(ptt_b,"toggled",G_CALLBACK(ptt_cb),NULL);
+
+    GtkWidget *ptt_ring_b=gtk_check_button_new_with_label("PTT On Ring");
+    gtk_widget_override_font(ptt_ring_b, pango_font_description_from_string("Arial 18"));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ptt_ring_b), (orion_mic&0x08)==ORION_MIC_PTT_RING_BIAS_TIP);
+    gtk_widget_show(ptt_ring_b);
+    gtk_grid_attach(GTK_GRID(grid),ptt_ring_b,0,4,1,1);
+    g_signal_connect(ptt_ring_b,"toggled",G_CALLBACK(ptt_ring_cb),NULL);
+
+    GtkWidget *bias_b=gtk_check_button_new_with_label("BIAS Enabled");
+    gtk_widget_override_font(bias_b, pango_font_description_from_string("Arial 18"));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (bias_b), (orion_mic&0x10)==ORION_MIC_BIAS_ENABLED);
+    gtk_widget_show(bias_b);
+    gtk_grid_attach(GTK_GRID(grid),bias_b,0,5,1,1);
+    g_signal_connect(bias_b,"toggled",G_CALLBACK(bias_cb),NULL);
+  }
+
+  GtkWidget *alex_b=gtk_check_button_new_with_label("ALEX");
+  gtk_widget_override_font(alex_b, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (alex_b), filter_board==ALEX);
+  gtk_widget_show(alex_b);
+  gtk_grid_attach(GTK_GRID(grid),alex_b,1,0,1,1);
+
+  GtkWidget *apollo_b=gtk_check_button_new_with_label("APOLLO");
+  gtk_widget_override_font(apollo_b, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apollo_b), filter_board==APOLLO);
+  gtk_widget_show(apollo_b);
+  gtk_grid_attach(GTK_GRID(grid),apollo_b,1,1,1,1);
+
+  g_signal_connect(alex_b,"toggled",G_CALLBACK(alex_cb),apollo_b);
+  g_signal_connect(apollo_b,"toggled",G_CALLBACK(apollo_cb),alex_b);
+
+  GtkWidget *apollo_tuner_b=gtk_check_button_new_with_label("Auto Tuner");
+  gtk_widget_override_font(apollo_tuner_b, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apollo_tuner_b), apollo_tuner);
+  gtk_widget_show(apollo_tuner_b);
+  gtk_grid_attach(GTK_GRID(grid),apollo_tuner_b,1,2,1,1);
+  g_signal_connect(apollo_tuner_b,"toggled",G_CALLBACK(apollo_tuner_cb),NULL);
+
+  GtkWidget *pa_b=gtk_check_button_new_with_label("PA");
+  gtk_widget_override_font(pa_b, pango_font_description_from_string("Arial 18"));
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pa_b), pa);
+  gtk_widget_show(pa_b);
+  gtk_grid_attach(GTK_GRID(grid),pa_b,1,3,1,1);
+  g_signal_connect(pa_b,"toggled",G_CALLBACK(pa_cb),NULL);
+
+  gtk_container_add(GTK_CONTAINER(content),grid);
+  GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK);
+  gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show_all(dialog);
+
+  g_signal_connect_swapped (dialog,
+                           "response",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
+
+  int result=gtk_dialog_run(GTK_DIALOG(dialog));
+}
+
+static void cw_cb(GtkWidget *widget, gpointer data) {
+  GtkWidget *dialog=gtk_dialog_new_with_buttons("CW",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+  GtkWidget *grid=gtk_grid_new();
+  //gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_row_homogeneous(GTK_GRID(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(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(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(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(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(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(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(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);
+
+/*
+int cw_keyer_speed=12; // 1-60 WPM
+int cw_keyer_weight=30; // 0-100
+int cw_keyer_spacing=0; // 0=on 1=off
+int cw_keyer_sidetone_volume=127; // 0-127
+int cw_keyer_ptt_delay=20; // 0-255ms
+int cw_keyer_hang_time=10; // ms
+int cw_keyer_sidetone_frequency=400; // Hz
+*/
+
+  gtk_container_add(GTK_CONTAINER(content),grid);
+  GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK);
+  gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show_all(dialog);
+
+  g_signal_connect_swapped (dialog,
+                           "response",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
+
+  int result=gtk_dialog_run(GTK_DIALOG(dialog));
+}
+
+static void adc_cb(GtkWidget *widget, gpointer data) {
+  int adc0=adc[0];
+  adc[0]=adc[1];
+  adc[1]=adc0;
+
+  char label[16];
+  gtk_grid_remove_row(GTK_GRID(toolbar_top),0);
+  
+  sprintf(label,"RX0=%d",adc[0]);
+  GtkWidget *rx0=gtk_label_new(label);
+  gtk_widget_override_font(rx0, pango_font_description_from_string("Arial 16"));
+  gtk_widget_show(rx0);
+  gtk_grid_attach(GTK_GRID(toolbar_top),rx0,0,0,1,1);
+  
+  sprintf(label,"RX1=%d",adc[1]);
+  GtkWidget *rx1=gtk_label_new(label);
+  gtk_widget_override_font(rx1, pango_font_description_from_string("Arial 16"));
+  gtk_widget_show(rx1);
+  gtk_grid_attach(GTK_GRID(toolbar_top),rx1,1,0,1,1);
+}
+
+static void lock_cb(GtkWidget *widget, gpointer data) {
+  locked=locked==1?0:1;
+  vfo_update(NULL);
+}
+
+static void mox_cb(GtkWidget *widget, gpointer data) {
+  gtk_grid_remove_row (GTK_GRID(toolbar_top),0);
+  if(getTune()==1) {
+    setTune(0);
+  }
+  setMox(getMox()==0?1:0);
+  vfo_update(NULL);
+  if(getMox()) {
+    mic_gain_label=gtk_label_new("Mic Gain:");
+    gtk_widget_override_font(mic_gain_label, pango_font_description_from_string("Arial 18"));
+    gtk_widget_show(mic_gain_label);
+    gtk_grid_attach(GTK_GRID(toolbar_top),mic_gain_label,0,0,1,1);
+
+    mic_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01);
+    gtk_range_set_value (GTK_RANGE(mic_scale),mic_gain);
+    gtk_widget_show(mic_scale);
+    gtk_grid_attach(GTK_GRID(toolbar_top),mic_scale,1,0,2,1);
+    g_signal_connect(G_OBJECT(mic_scale),"value_changed",G_CALLBACK(micgain_value_changed_cb),NULL);
+
+    GtkWidget *drive_label=gtk_label_new("Drive:");
+    gtk_widget_override_font(drive_label, pango_font_description_from_string("Arial 18"));
+    gtk_widget_show(drive_label);
+    gtk_grid_attach(GTK_GRID(toolbar_top),drive_label,3,0,1,1);
+
+    drive_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01);
+    gtk_range_set_value (GTK_RANGE(drive_scale),getDrive());
+    gtk_widget_show(drive_scale);
+    gtk_grid_attach(GTK_GRID(toolbar_top),drive_scale,4,0,2,1);
+    g_signal_connect(G_OBJECT(drive_scale),"value_changed",G_CALLBACK(drive_value_changed_cb),NULL);
+  } else {
+    af_gain_label=gtk_label_new("AF:");
+    gtk_widget_override_font(af_gain_label, pango_font_description_from_string("Arial 18"));
+    gtk_widget_show(af_gain_label);
+    gtk_grid_attach(GTK_GRID(toolbar_top),af_gain_label,0,0,1,1);
+  
+    audio_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01);
+    gtk_range_set_value (GTK_RANGE(audio_scale),volume);
+    gtk_widget_show(audio_scale);
+    gtk_grid_attach(GTK_GRID(toolbar_top),audio_scale,1,0,2,1);
+    g_signal_connect(G_OBJECT(audio_scale),"value_changed",G_CALLBACK(afgain_value_changed_cb),NULL);
+
+    agc_gain_label=gtk_label_new("AGC:");
+    gtk_widget_override_font(agc_gain_label, pango_font_description_from_string("Arial 18"));
+    gtk_widget_show(agc_gain_label);
+    gtk_grid_attach(GTK_GRID(toolbar_top),agc_gain_label,3,0,1,1);
+
+    agc_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 120.0, 1.0);
+    gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain);
+    gtk_widget_show(agc_scale);
+    gtk_grid_attach(GTK_GRID(toolbar_top),agc_scale,4,0,2,1);
+    g_signal_connect(G_OBJECT(agc_scale),"value_changed",G_CALLBACK(agcgain_value_changed_cb),NULL);
+
+  }
+  //gtk_widget_queue_draw(toolbar_top);
+}
+
+int ptt_update(void *data) {
+  BANDSTACK_ENTRY *entry;
+  entry=bandstack_entry_get_current();
+  int ptt=(int)data;
+  if((entry->mode==modeCWL || entry->mode==modeCWU) && cw_keyer_internal==1) {
+    if(ptt!= getMox()) {
+      mox_cb(NULL,NULL);
+    }
+  } else {
+    mox_cb(NULL,NULL);
+  }
+  return 0;
+}
+
+static void tune_cb(GtkWidget *widget, gpointer data) {
+  gtk_grid_remove_row (GTK_GRID(toolbar_top),0);
+  if(getMox()==1) {
+    setMox(0);
+  }
+  setTune(getTune()==0?1:0);
+  vfo_update(NULL);
+  if(getTune()) {
+    GtkWidget *tune_label=gtk_label_new("Tune Drive:");
+    gtk_widget_override_font(tune_label, pango_font_description_from_string("Arial 18"));
+    gtk_widget_show(tune_label);
+    gtk_grid_attach(GTK_GRID(toolbar_top),tune_label,0,0,1,1);
+
+    tune_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01);
+    gtk_range_set_value (GTK_RANGE(tune_scale),getTuneDrive());
+    gtk_widget_show(tune_scale);
+    gtk_grid_attach(GTK_GRID(toolbar_top),tune_scale,1,0,2,1);
+    g_signal_connect(G_OBJECT(tune_scale),"value_changed",G_CALLBACK(tune_value_changed_cb),NULL);
+  } else {
+    af_gain_label=gtk_label_new("AF:");
+    gtk_widget_override_font(af_gain_label, pango_font_description_from_string("Arial 18"));
+    gtk_widget_show(af_gain_label);
+    gtk_grid_attach(GTK_GRID(toolbar_top),af_gain_label,0,0,1,1);
+
+    audio_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01);
+    gtk_range_set_value (GTK_RANGE(audio_scale),volume);
+    gtk_widget_show(audio_scale);
+    gtk_grid_attach(GTK_GRID(toolbar_top),audio_scale,1,0,2,1);
+    g_signal_connect(G_OBJECT(audio_scale),"value_changed",G_CALLBACK(afgain_value_changed_cb),NULL);
+
+    agc_gain_label=gtk_label_new("AGC:");
+    gtk_widget_override_font(agc_gain_label, pango_font_description_from_string("Arial 18"));
+    gtk_widget_show(agc_gain_label);
+    gtk_grid_attach(GTK_GRID(toolbar_top),agc_gain_label,3,0,1,1);
+
+    agc_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 120.0, 1.0);
+    gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain);
+    gtk_widget_show(agc_scale);
+    gtk_grid_attach(GTK_GRID(toolbar_top),agc_scale,4,0,2,1);
+    g_signal_connect(G_OBJECT(agc_scale),"value_changed",G_CALLBACK(agcgain_value_changed_cb),NULL);
+  }
+}
+
+GtkWidget *toolbar_init(int my_width, int my_height, GtkWidget* parent) {
+    width=my_width;
+    height=my_height;
+    parent_window=parent;
+
+    white.red=1.0;
+    white.green=1.0;
+    white.blue=1.0;
+    white.alpha=0.0;
+
+    gray.red=0.25;
+    gray.green=0.25;
+    gray.blue=0.25;
+    gray.alpha=0.0;
+
+    toolbar=gtk_grid_new();
+    gtk_widget_set_size_request (toolbar, width, height);
+    gtk_grid_set_row_homogeneous(GTK_GRID(toolbar), TRUE);
+
+    toolbar_top=gtk_grid_new();
+    gtk_grid_set_column_homogeneous(GTK_GRID(toolbar_top),TRUE);
+
+    toolbar_bottom=gtk_grid_new();
+    gtk_grid_set_column_homogeneous(GTK_GRID(toolbar_bottom),FALSE);
+    
+    GtkWidget *band=gtk_button_new_with_label("Band");
+    gtk_widget_override_font(band, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(band),"clicked",G_CALLBACK(band_cb),NULL);
+    gtk_widget_show(band);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),band,column,0,1,1);
+    column++;
+
+    GtkWidget *mode=gtk_button_new_with_label("Mode");
+    gtk_widget_override_font(mode, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(mode),"clicked",G_CALLBACK(mode_cb),NULL);
+    gtk_widget_show(mode);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),mode,column,0,1,1);
+    column++;
+
+    GtkWidget *filter=gtk_button_new_with_label("Filter");
+    gtk_widget_override_font(filter, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(filter),"clicked",G_CALLBACK(filter_cb),NULL);
+    gtk_widget_show(filter);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),filter,column,0,1,1);
+    column++;
+
+    GtkWidget *audio=gtk_button_new_with_label("Audio");
+    gtk_widget_override_font(audio, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(audio),"clicked",G_CALLBACK(audio_cb),NULL);
+    gtk_widget_show(audio);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),audio,column,0,1,1);
+    column++;
+
+    GtkWidget *config=gtk_button_new_with_label("Config");
+    gtk_widget_override_font(config, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(config),"clicked",G_CALLBACK(config_cb),NULL);
+    gtk_widget_show(config);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),config,column,0,1,1);
+    column++;
+
+    GtkWidget *cw=gtk_button_new_with_label("CW");
+    gtk_widget_override_font(cw, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(cw),"clicked",G_CALLBACK(cw_cb),NULL);
+    gtk_widget_show(cw);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),cw,column,0,1,1);
+    column++;
+
+    GtkWidget *exit=gtk_button_new_with_label("Exit");
+    gtk_widget_override_font(exit, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(exit),"clicked",G_CALLBACK(exit_cb),NULL);
+    gtk_widget_show(exit);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),exit,column,0,1,1);
+    column++;
+
+    GtkWidget *lock=gtk_button_new_with_label("Lock");
+    gtk_widget_override_font(lock, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(lock),"clicked",G_CALLBACK(lock_cb),NULL);
+    gtk_widget_show(lock);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),lock,column,0,1,1);
+    column++;
+
+    GtkWidget *tune=gtk_button_new_with_label("Tune");
+    gtk_widget_override_font(tune, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(tune),"clicked",G_CALLBACK(tune_cb),NULL);
+    gtk_widget_show(tune);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),tune,column,0,1,1);
+    column++;
+
+    GtkWidget *tx=gtk_button_new_with_label("Mox");
+    gtk_widget_override_font(tx, pango_font_description_from_string("Arial 20"));
+    g_signal_connect(G_OBJECT(tx),"clicked",G_CALLBACK(mox_cb),NULL);
+    gtk_widget_show(tx);
+    gtk_grid_attach(GTK_GRID(toolbar_bottom),tx,column,0,1,1);
+    column++;
+
+
+  // default to receive controls on top bar
+  af_gain_label=gtk_label_new("AF:");
+  gtk_widget_override_font(af_gain_label, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show(af_gain_label);
+  gtk_grid_attach(GTK_GRID(toolbar_top),af_gain_label,0,0,1,1);
+
+  audio_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01);
+  gtk_range_set_value (GTK_RANGE(audio_scale),volume);
+  gtk_widget_show(audio_scale);
+  gtk_grid_attach(GTK_GRID(toolbar_top),audio_scale,1,0,2,1);
+  g_signal_connect(G_OBJECT(audio_scale),"value_changed",G_CALLBACK(afgain_value_changed_cb),NULL);
+
+  agc_gain_label=gtk_label_new("AGC:");
+  gtk_widget_override_font(agc_gain_label, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show(agc_gain_label);
+  gtk_grid_attach(GTK_GRID(toolbar_top),agc_gain_label,3,0,1,1);
+
+  agc_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 120.0, 1.0);
+  gtk_range_set_value (GTK_RANGE(agc_scale),agc_gain);
+  gtk_widget_show(agc_scale);
+  gtk_grid_attach(GTK_GRID(toolbar_top),agc_scale,4,0,2,1);
+  g_signal_connect(G_OBJECT(agc_scale),"value_changed",G_CALLBACK(agcgain_value_changed_cb),NULL);
+
+/*
+
+  GtkWidget *drive_label=gtk_label_new("Drive:");
+  gtk_widget_override_font(drive_label, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show(drive_label);
+  gtk_grid_attach(toolbar_top,drive_label,6,0,1,1);
+
+  drive_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01);
+  gtk_range_set_value (drive_scale,getTuneDrive());
+  gtk_widget_show(drive_scale);
+  gtk_grid_attach(toolbar_top,drive_scale,7,0,2,1);
+  g_signal_connect(G_OBJECT(drive_scale),"value_changed",G_CALLBACK(drive_value_changed_cb),NULL);
+
+  GtkWidget *tune_label=gtk_label_new("Tune:");
+  gtk_widget_override_font(tune_label, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show(tune_label);
+  gtk_grid_attach(toolbar_top,tune_label,9,0,1,1);
+
+  tune_scale=gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,0.0, 1.0, 0.01);
+  gtk_range_set_value (tune_scale,getTuneDrive());
+  gtk_widget_show(tune_scale);
+  gtk_grid_attach(toolbar_top,tune_scale,10,0,2,1);
+  g_signal_connect(G_OBJECT(tune_scale),"value_changed",G_CALLBACK(tune_value_changed_cb),NULL);
+*/
+
+  gtk_grid_attach(GTK_GRID(toolbar),toolbar_top,0,0,1,1);
+  gtk_widget_show(toolbar_top);
+  gtk_grid_attach(GTK_GRID(toolbar),toolbar_bottom,0,1,1,1);
+  gtk_widget_show(toolbar_bottom);
+
+  return toolbar;
+}
diff --git a/toolbar.h b/toolbar.h
new file mode 100644 (file)
index 0000000..53c25f3
--- /dev/null
+++ b/toolbar.h
@@ -0,0 +1,6 @@
+void set_agc_gain(double value);
+void set_af_gain(double value);
+void set_drive(double drive);
+void set_tune(double tune);
+int ptt_update(void *data);
+GtkWidget *toolbar_init(int my_width, int my_height, GtkWidget* parent);
diff --git a/vfo.c b/vfo.c
new file mode 100644 (file)
index 0000000..648f31d
--- /dev/null
+++ b/vfo.c
@@ -0,0 +1,384 @@
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <math.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "mode.h"
+#include "filter.h"
+#include "bandstack.h"
+#include "band.h"
+#include "new_protocol.h"
+#include "rotary_encoder.h"
+#include "radio.h"
+#include "vfo.h"
+#include "channel.h"
+#include "wdsp.h"
+
+static GtkWidget *parent_window;
+
+static GtkWidget *vfo;
+static cairo_surface_t *vfo_surface = NULL;
+
+static pthread_t rotary_encoder_thread_id;
+
+static int steps[]={1,10,25,50,100,250,500,1000,2500,5000,6250,9000,10000,12500,15000,20000,25000,30000,50000,100000,0};
+static char *step_labels[]={"1Hz","10Hz","25Hz","50Hz","100Hz","250Hz","500Hz","1kHz","2.5kHz","5kHz","6.25kHz","9kHz","10kHz","12.5kHz","15kHz","20kHz","25kHz","30kHz","50kHz","100kHz",0};
+static int af_function=0;
+static int previous_af_function=0;
+static int rf_function=0;
+static int previous_rf_function=0;
+static int band_button=0;
+static int previous_band_button=0;
+
+static GtkWidget* menu=NULL;
+static GtkWidget* band_menu=NULL;
+
+void vfo_step(int steps) {
+  if(!locked) {
+    BANDSTACK_ENTRY* entry=bandstack_entry_get_current();
+    entry->frequencyA=entry->frequencyA+(steps*step);
+    setFrequency(entry->frequencyA);
+    vfo_update(NULL);
+  }
+}
+
+void vfo_move(int hz) {
+  if(!locked) {
+    BANDSTACK_ENTRY* entry=bandstack_entry_get_current();
+    entry->frequencyA=(entry->frequencyA+hz)/step*step;
+    setFrequency(entry->frequencyA);
+    vfo_update(NULL);
+  }
+}
+
+static int rotary_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);
+    vfo_update(NULL);
+  }
+  free(data);
+  return 0;
+}
+
+static int af_encoder_changed(void *data) {
+  int pos=*(int*)data;
+  if(pos!=0) {
+    if(af_function) {
+      // agc gain
+      double gain=agc_gain;
+      gain+=(double)pos;
+      if(gain<0.0) {
+        gain=0.0;
+      } else if(gain>120.0) {
+        gain=120.0;
+      }
+      set_agc_gain(gain);
+    } else {
+      // af gain
+      double gain=volume;
+      gain+=(double)pos/20.0;
+      if(gain<0.0) {
+        gain=0.0;
+      } else if(gain>1.0) {
+        gain=1.0;
+      }
+      set_af_gain(gain);
+    }
+  }
+  free(data);
+  return 0;
+}
+
+static int rf_encoder_changed(void *data) {
+  int pos=*(int*)data;
+  if(pos!=0) {
+    if(rf_function) {
+      // tune drive
+      double d=getTuneDrive();
+      d+=(double)pos/10.0;
+      if(d<0.0) {
+        d=0.0;
+      } else if(d>1.0) {
+        d=1.0;
+      }
+      set_tune(d);
+    } else {
+      // drive
+      double d=getDrive();
+      d+=(double)pos/10.0;
+      if(d<0.0) {
+        d=0.0;
+      } else if(d>1.0) {
+        d=1.0;
+      }
+      set_drive(d);
+    }
+  }
+  free(data);
+  return 0;
+}
+
+static int band_pressed(void *data) {
+  int function=*(int*)data;
+
+  BANDSTACK_ENTRY *entry;
+  if(function) {
+    entry=bandstack_entry_next();
+  } else {
+    int b=band_get_current();
+    b++;
+    if(b>=BANDS) {
+      b=0;
+    }
+    BAND* band=band_set_current(b);
+    entry=bandstack_entry_get_current();
+  }
+
+  setFrequency(entry->frequencyA);
+  setMode(entry->mode);
+  FILTER* band_filters=filters[entry->mode];
+  FILTER* band_filter=&band_filters[entry->filter];
+  setFilter(band_filter->low,band_filter->high);
+
+  BAND *band=band_get_current_band();
+  set_alex_rx_antenna(band->alexRxAntenna);
+  set_alex_tx_antenna(band->alexTxAntenna);
+  set_alex_attenuation(band->alexAttenuation);
+  vfo_update(NULL);
+
+
+  free(data);
+  return 0;
+}
+
+static void* rotary_encoder_thread(void *arg) {
+    int pos;
+    while(1) {
+
+        pos=vfo_encoder_get_pos();
+        if(pos!=0) {
+            int *p=malloc(sizeof(int));
+            *p=pos;
+            g_idle_add(rotary_encoder_changed,(gpointer)p);
+        }
+
+        af_function=af_function_get_state();
+        if(af_function!=previous_af_function) {
+            fprintf(stderr,"af_function: %d\n",af_function);
+            previous_af_function=af_function;
+        }
+        pos=af_encoder_get_pos();
+        if(pos!=0) {
+            int *p=malloc(sizeof(int));
+            *p=pos;
+            g_idle_add(af_encoder_changed,(gpointer)p);
+        }
+
+        rf_function=rf_function_get_state();
+        if(rf_function!=previous_rf_function) {
+            fprintf(stderr,"rf_function: %d\n",rf_function);
+            previous_rf_function=rf_function;
+        }
+        pos=rf_encoder_get_pos();
+        if(pos!=0) {
+            int *p=malloc(sizeof(int));
+            *p=pos;
+            g_idle_add(rf_encoder_changed,(gpointer)p);
+        }
+
+        int band_button=band_get_state();
+        if(band_button!=previous_band_button) {
+            fprintf(stderr,"band_button: %d\n",band_button);
+            previous_band_button=band_button;
+            if(band_button) {
+                int function=function_get_state();
+                g_idle_add(band_pressed,(gpointer)function);
+            }
+        }
+
+#ifdef raspberrypi
+        gpioDelay(100000); // 10 per second
+#endif
+#ifdef odroid
+        usleep(100000);
+#endif
+    }
+}
+
+static gboolean vfo_configure_event_cb (GtkWidget         *widget,
+            GdkEventConfigure *event,
+            gpointer           data)
+{
+  if (vfo_surface)
+    cairo_surface_destroy (vfo_surface);
+
+  vfo_surface = gdk_window_create_similar_surface (gtk_widget_get_window (widget),
+                                       CAIRO_CONTENT_COLOR,
+                                       gtk_widget_get_allocated_width (widget),
+                                       gtk_widget_get_allocated_height (widget));
+
+  /* Initialize the surface to black */
+  cairo_t *cr;
+  cr = cairo_create (vfo_surface);
+  cairo_set_source_rgb (cr, 0, 0, 0);
+  cairo_paint (cr);
+
+  /* We've handled the configure event, no need for further processing. */
+  return TRUE;
+}
+
+static gboolean vfo_draw_cb (GtkWidget *widget,
+ cairo_t   *cr,
+ gpointer   data)
+{
+  cairo_set_source_surface (cr, vfo_surface, 0, 0);
+  cairo_paint (cr);
+
+  return FALSE;
+}
+
+int vfo_update(void *data) {
+    BANDSTACK_ENTRY* entry=bandstack_entry_get_current();
+    FILTER* band_filters=filters[entry->mode];
+    FILTER* band_filter=&band_filters[entry->filter];
+    if(vfo_surface) {
+        cairo_t *cr;
+        cr = cairo_create (vfo_surface);
+        cairo_set_source_rgb (cr, 0, 0, 0);
+        cairo_paint (cr);
+
+        cairo_select_font_face(cr, "Arial",
+            CAIRO_FONT_SLANT_NORMAL,
+            CAIRO_FONT_WEIGHT_BOLD);
+        cairo_set_font_size(cr, 36);
+
+        if(isTransmitting()) {
+            cairo_set_source_rgb(cr, 1, 0, 0);
+        } else {
+            cairo_set_source_rgb(cr, 0, 1, 0);
+        }
+
+        char sf[32];
+        sprintf(sf,"%0lld.%06lld MHz %s %s",entry->frequencyA/(long long)1000000,entry->frequencyA%(long long)1000000,mode_string[entry->mode],band_filter->title);
+        cairo_move_to(cr, 130, 45);  
+        cairo_show_text(cr, sf);
+
+        cairo_set_font_size(cr, 18);
+        sprintf(sf,"Step %dHz",step);
+        cairo_move_to(cr, 10, 25);  
+        cairo_show_text(cr, sf);
+
+        if(locked) {
+            cairo_set_source_rgb(cr, 1, 0, 0);
+            cairo_move_to(cr, 10, 50);  
+            cairo_show_text(cr, "Locked");
+        }
+
+        cairo_destroy (cr);
+        gtk_widget_queue_draw (vfo);
+    }
+    return 0;
+}
+
+static gboolean
+vfo_step_select_cb (GtkWidget *widget,
+               gpointer        data)
+{
+  step=steps[(int)data];
+  vfo_update(NULL);
+}
+
+static gboolean
+vfo_press_event_cb (GtkWidget *widget,
+               GdkEventButton *event,
+               gpointer        data)
+{
+  GtkWidget *dialog=gtk_dialog_new_with_buttons("VFO",GTK_WINDOW(parent_window),GTK_DIALOG_DESTROY_WITH_PARENT,NULL,NULL);
+
+  GtkWidget *content=gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+  GtkWidget *grid=gtk_grid_new();
+
+  gtk_grid_set_column_homogeneous(GTK_GRID(grid),TRUE);
+  gtk_grid_set_row_homogeneous(GTK_GRID(grid),TRUE);
+
+  GtkWidget *step_rb=NULL;
+  int i=0;
+  while(steps[i]!=0) {
+    if(i==0) {
+        step_rb=gtk_radio_button_new_with_label(NULL,step_labels[i]);
+    } else {
+        step_rb=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(step_rb),step_labels[i]);
+    }
+    gtk_widget_override_font(step_rb, pango_font_description_from_string("Arial 18"));
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (step_rb), steps[i]==step);
+    gtk_widget_show(step_rb);
+    gtk_grid_attach(GTK_GRID(grid),step_rb,i/5,i%5,1,1);
+    g_signal_connect(step_rb,"pressed",G_CALLBACK(vfo_step_select_cb),(gpointer *)i);
+    i++;
+  }
+
+  gtk_container_add(GTK_CONTAINER(content),grid);
+  GtkWidget *close_button=gtk_dialog_add_button(GTK_DIALOG(dialog),"Close",GTK_RESPONSE_OK);
+  gtk_widget_override_font(close_button, pango_font_description_from_string("Arial 18"));
+  gtk_widget_show_all(dialog);
+
+  g_signal_connect_swapped (dialog,
+                           "response",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
+
+  int result=gtk_dialog_run(GTK_DIALOG(dialog));
+  
+  return TRUE;
+}
+
+GtkWidget* vfo_init(int width,int height,GtkWidget *parent) {
+
+  parent_window=parent;
+
+  vfo = gtk_drawing_area_new ();
+  /* set a minimum size */
+  gtk_widget_set_size_request (vfo, width, height);
+
+  /* Signals used to handle the backing surface */
+  g_signal_connect (vfo, "draw",
+            G_CALLBACK (vfo_draw_cb), NULL);
+  g_signal_connect (vfo,"configure-event",
+            G_CALLBACK (vfo_configure_event_cb), NULL);
+
+  if(encoder_init() == 0) {
+    int rc=pthread_create(&rotary_encoder_thread_id, NULL, rotary_encoder_thread, NULL);
+    if(rc<0) {
+      fprintf(stderr,"pthread_create for rotary_encoder_thread failed %d\n",rc);
+    }
+  } else {
+    fprintf(stderr,"encoder_init failed\n");
+  }
+
+  /* Event signals */
+  g_signal_connect (vfo, "button-press-event",
+            G_CALLBACK (vfo_press_event_cb), NULL);
+  gtk_widget_set_events (vfo, gtk_widget_get_events (vfo)
+                     | GDK_BUTTON_PRESS_MASK);
+
+  BAND *band=band_get_current_band();
+  BANDSTACK_ENTRY* entry=bandstack_entry_get_current();
+  setFrequency(entry->frequencyA);
+  setMode(entry->mode);
+  FILTER* band_filters=filters[entry->mode];
+  FILTER* band_filter=&band_filters[entry->filter];
+  setFilter(band_filter->low,band_filter->high);
+
+  set_alex_rx_antenna(band->alexRxAntenna);
+  set_alex_tx_antenna(band->alexTxAntenna);
+  set_alex_attenuation(band->alexAttenuation);
+
+  return vfo;
+}
diff --git a/vfo.h b/vfo.h
new file mode 100644 (file)
index 0000000..22d2da1
--- /dev/null
+++ b/vfo.h
@@ -0,0 +1,5 @@
+
+GtkWidget* vfo_init(int width,int height,GtkWidget *parent);
+void vfo_step(int steps);
+void vfo_move(int hz);
+int vfo_update(void*);
diff --git a/waterfall.c b/waterfall.c
new file mode 100644 (file)
index 0000000..176e868
--- /dev/null
@@ -0,0 +1,245 @@
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <math.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <string.h>
+#include "vfo.h"
+#include "new_protocol.h"
+#include "waterfall.h"
+
+static GtkWidget *waterfall;
+static GdkPixbuf *pixbuf = NULL;
+
+static float highThreshold = -100.0f;
+static float lowThreshold = -150.0f;
+
+static int colorLowR=0; // black
+static int colorLowG=0;
+static int colorLowB=0;
+
+static int colorMidR=255; // red
+static int colorMidG=0;
+static int colorMidB=0;
+
+static int colorHighR=255; // yellow
+static int colorHighG=255;
+static int colorHighB=0;
+
+
+static gint first_x;
+static gint last_x;
+static gboolean has_moved=FALSE;
+static gboolean pressed=FALSE;
+
+static gfloat hz_per_pixel;
+
+#define BANDS 7
+
+static long long frequency[BANDS];
+static gint mode[BANDS];
+static gint band=4;
+
+static int display_width;
+static int waterfall_height;
+
+/* Create a new surface of the appropriate size to store our scribbles */
+static gboolean
+waterfall_configure_event_cb (GtkWidget         *widget,
+            GdkEventConfigure *event,
+            gpointer           data)
+{
+  return TRUE;
+}
+
+/* Redraw the screen from the surface. Note that the ::draw
+ * signal receives a ready-to-be-used cairo_t that is already
+ * clipped to only draw the exposed areas of the widget
+ */
+static gboolean
+waterfall_draw_cb (GtkWidget *widget,
+ cairo_t   *cr,
+ gpointer   data)
+{
+  gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+  cairo_paint (cr);
+  return TRUE;
+}
+
+static gboolean
+waterfall_button_press_event_cb (GtkWidget      *widget,
+               GdkEventButton *event,
+               gpointer        data)
+{
+  int x=(int)event->x;
+  if (event->button == 1) {
+    last_x=(int)event->x;
+    has_moved=FALSE;
+    pressed=TRUE;
+  }
+  return TRUE;
+}
+
+static gboolean
+waterfall_button_release_event_cb (GtkWidget      *widget,
+               GdkEventButton *event,
+               gpointer        data)
+{
+  int x=(int)event->x;
+  if (event->button == 1) {
+    if(has_moved) {
+      // drag
+      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));
+    }
+    last_x=x;
+    pressed=FALSE;
+  }
+  return TRUE;
+}
+
+static gboolean
+waterfall_motion_notify_event_cb (GtkWidget      *widget,
+                GdkEventMotion *event,
+                gpointer        data)
+{
+  int x, y;
+  GdkModifierType state;
+  gdk_window_get_device_position (event->window,
+                                event->device,
+                                &x,
+                                &y,
+                                &state);
+  if((state & GDK_BUTTON1_MASK == GDK_BUTTON1_MASK) || pressed) {
+    int moved=last_x-x;
+    vfo_move((int)((float)moved*hz_per_pixel));
+    last_x=x;
+    has_moved=TRUE;
+  }
+
+  return TRUE;
+}
+
+void waterfall_update(float *data) {
+
+
+    int i;
+
+    char *pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+    int width=gdk_pixbuf_get_width(pixbuf);
+    int height=gdk_pixbuf_get_height(pixbuf);
+    int rowstride=gdk_pixbuf_get_rowstride(pixbuf);
+    int channels=gdk_pixbuf_get_n_channels(pixbuf);
+
+    memmove(&pixels[rowstride],pixels,(height-1)*rowstride);
+
+    float sample;
+    int average=0;
+    char *p;
+    p=pixels;
+    for(i=0;i<width;i++) {
+            sample=data[i]+get_attenuation();
+            average+=(int)sample;
+            if(sample<lowThreshold) {
+                *p++=colorLowR;
+                *p++=colorLowG;
+                *p++=colorLowB;
+            } else if(sample>highThreshold) {
+                *p++=colorHighR;
+                *p++=colorHighG;
+                *p++=colorHighB;
+            } else {
+                float range=highThreshold-lowThreshold;
+                float offset=sample-lowThreshold;
+                float percent=offset/range;
+                if(percent<(2.0f/9.0f)) {
+                    float local_percent = percent / (2.0f/9.0f);
+                    *p++ = (int)((1.0f-local_percent)*colorLowR);
+                    *p++ = (int)((1.0f-local_percent)*colorLowG);
+                    *p++ = (int)(colorLowB + local_percent*(255-colorLowB));
+                } else if(percent<(3.0f/9.0f)) {
+                    float local_percent = (percent - 2.0f/9.0f) / (1.0f/9.0f);
+                    *p++ = 0;
+                    *p++ = (int)(local_percent*255);
+                    *p++ = 255;
+                } else if(percent<(4.0f/9.0f)) {
+                     float local_percent = (percent - 3.0f/9.0f) / (1.0f/9.0f);
+                     *p++ = 0;
+                     *p++ = 255;
+                     *p++ = (int)((1.0f-local_percent)*255);
+                } else if(percent<(5.0f/9.0f)) {
+                     float local_percent = (percent - 4.0f/9.0f) / (1.0f/9.0f);
+                     *p++ = (int)(local_percent*255);
+                     *p++ = 255;
+                     *p++ = 0;
+                } else if(percent<(7.0f/9.0f)) {
+                     float local_percent = (percent - 5.0f/9.0f) / (2.0f/9.0f);
+                     *p++ = 255;
+                     *p++ = (int)((1.0f-local_percent)*255);
+                     *p++ = 0;
+                } else if(percent<(8.0f/9.0f)) {
+                     float local_percent = (percent - 7.0f/9.0f) / (1.0f/9.0f);
+                     *p++ = 255;
+                     *p++ = 0;
+                     *p++ = (int)(local_percent*255);
+                } else {
+                     float local_percent = (percent - 8.0f/9.0f) / (1.0f/9.0f);
+                     *p++ = (int)((0.75f + 0.25f*(1.0f-local_percent))*255.0f);
+                     *p++ = (int)(local_percent*255.0f*0.5f);
+                     *p++ = 255;
+                }
+            }
+        
+    }
+
+    
+    lowThreshold=(float)((average/display_width));
+    highThreshold=lowThreshold+50.0;
+
+    gtk_widget_queue_draw (waterfall);
+
+}
+
+GtkWidget* waterfall_init(int width,int height) {
+  display_width=width;
+  waterfall_height=height;
+
+  hz_per_pixel=(double)getSampleRate()/(double)display_width;
+
+  //waterfall_frame = gtk_frame_new (NULL);
+  waterfall = gtk_drawing_area_new ();
+  gtk_widget_set_size_request (waterfall, width, height);
+
+  /* Signals used to handle the backing surface */
+  g_signal_connect (waterfall, "draw",
+            G_CALLBACK (waterfall_draw_cb), NULL);
+  g_signal_connect (waterfall,"configure-event",
+            G_CALLBACK (waterfall_configure_event_cb), NULL);
+
+  pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, width, height);
+
+  /* Event signals */
+  g_signal_connect (waterfall, "motion-notify-event",
+            G_CALLBACK (waterfall_motion_notify_event_cb), NULL);
+  g_signal_connect (waterfall, "button-press-event",
+            G_CALLBACK (waterfall_button_press_event_cb), NULL);
+  g_signal_connect (waterfall, "button-release-event",
+            G_CALLBACK (waterfall_button_release_event_cb), NULL);
+
+  /* Ask to receive events the drawing area doesn't normally
+   * subscribe to. In particular, we need to ask for the
+   * button press and motion notify events that want to handle.
+   */
+  gtk_widget_set_events (waterfall, gtk_widget_get_events (waterfall)
+                     | GDK_BUTTON_PRESS_MASK
+                     | GDK_BUTTON_RELEASE_MASK
+                     | GDK_BUTTON1_MOTION_MASK
+                     | GDK_POINTER_MOTION_MASK
+                     | GDK_POINTER_MOTION_HINT_MASK);
+
+  return waterfall;
+}
diff --git a/waterfall.h b/waterfall.h
new file mode 100644 (file)
index 0000000..7b5e379
--- /dev/null
@@ -0,0 +1,3 @@
+
+void waterfall_update(float *data);
+GtkWidget* waterfall_init(int width,int height);
diff --git a/xvtr.h b/xvtr.h
new file mode 100644 (file)
index 0000000..bc948d6
--- /dev/null
+++ b/xvtr.h
@@ -0,0 +1,56 @@
+/**
+* @file xvtr.h
+* @brief XVTR definition files
+* @author John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* @version 0.1
+* @date 2009-04-11
+*/
+// xvtr.h
+
+/* Copyright (C)
+* This program is free software; you can redistribute it and/or2009 - John Melton, G0ORX/N6LYT, Doxygen Comments Dave Larsen, KV0S
+* 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.
+*
+*/
+
+/* --------------------------------------------------------------------------*/
+/**
+* @brief XVTR definition
+*/
+struct _XVTR_ENTRY {
+    char name[32];
+    long long rxFrequency;
+    long long rxFrequencyMin;
+    long long rxFrequencyMax;
+    long long rxFrequencyLO;
+    long long txFrequency;
+    long long txFrequencyMin;
+    long long txFrequencyMax;
+    long long txFrequencyLO;
+    int fullDuplex;
+    int mode;
+    int filter;
+    int var1Low;
+    int var1High;
+    int var2Low;
+    int var2High;
+    int step;
+    int preamp;
+    int alexRxAntenna;
+    int alexTxAntenna;
+    int alexAttenuation;
+};
+
+typedef struct _XVTR_ENTRY XVTR_ENTRY;
+