# uncomment the line below to include support for Pi SDR
#PI_SDR_INCLUDE=PI_SDR
+# uncomment the line below to include MIDI support
+MIDI_INCLUDE=MIDI
+
#uncomment the line below for the platform being compiled on (actually not used)
UNAME_N=raspberrypi
#UNAME_N=odroid
CC=gcc
LINK=gcc
+ifeq ($(MIDI_INCLUDE),MIDI)
+MIDI_OPTIONS=-D MIDI
+MIDI_SOURCES= mac_midi.c midi2.c midi3.c
+MIDI_HEADERS= midi.h
+MIDI_OBJS= mac_midi.o midi2.o midi3.o
+MIDI_LIBS= -framework CoreMIDI -framework Foundation
+endif
+
ifeq ($(PURESIGNAL_INCLUDE),PURESIGNAL)
PURESIGNAL_OPTIONS=-D PURESIGNAL
PURESIGNAL_SOURCES= \
PORTAUDIO_OPTIONS=-DPORTAUDIO
AUDIO_LIBS=-lportaudio
-OPTIONS=-g -Wno-deprecated-declarations $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(USBOZY_OPTIONS) $(I2C_OPTIONS) $(GPIO_OPTIONS) $(LIMESDR_OPTIONS) \
+OPTIONS=-g -Wno-deprecated-declarations $(MIDI_OPTIONS) $(PURESIGNAL_OPTIONS) $(REMOTE_OPTIONS) $(USBOZY_OPTIONS) $(I2C_OPTIONS) $(GPIO_OPTIONS) $(LIMESDR_OPTIONS) \
$(FREEDV_OPTIONS) $(LOCALCW_OPTIONS) $(RADIOBERRY_OPTIONS) $(PI_SDR_OPTIONS) $(PSK_OPTIONS) $(STEMLAB_OPTIONS) $(STEMLAB_FIX_OPTION) \
-D GIT_DATE='"$(GIT_DATE)"' -D GIT_VERSION='"$(GIT_VERSION)"' $(PORTAUDIO_OPTIONS) $(DEBUG_OPTION) -O3
-LIBS=-lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(PSKLIBS) $(GTKLIBS) $(GPIO_LIBS) $(SOAPYSDRLIBS) $(FREEDVLIBS) $(STEMLAB_LIBS)
+LIBS=-lm -lwdsp -lpthread $(AUDIO_LIBS) $(USBOZY_LIBS) $(PSKLIBS) $(GTKLIBS) $(GPIO_LIBS) $(SOAPYSDRLIBS) $(FREEDVLIBS) $(STEMLAB_LIBS) $(MIDI_LIBS)
INCLUDES=$(GTKINCLUDES)
COMPILE=$(CC) $(OPTIONS) $(INCLUDES)
cwramp.o \
portaudio.o
-$(PROGRAM): $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS)
- $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(STEMLAB_OBJS) $(LIBS)
+$(PROGRAM): $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(MIDI_OBJS) $(STEMLAB_OBJS)
+ $(LINK) -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) $(MIDI_OBJS) $(STEMLAB_OBJS) $(LIBS)
-all: prebuild $(PROGRAM) $(HEADERS) $(REMOTE_HEADERS) $(USBOZY_HEADERS) $(LIMESDR_HEADERS) $(FREEDV_HEADERS) $(LOCALCW_HEADERS) $(I2C_HEADERS) $(GPIO_HEADERS) $(PSK_HEADERS) $(PURESIGNAL_HEADERS) $(STEMLAB_HEADERS) $(SOURCES) $(REMOTE_SOURCES) $(USBOZY_SOURCES) $(LIMESDR_SOURCES) $(FREEDV_SOURCES) $(I2C_SOURCES) $(GPIO_SOURCES) $(PSK_SOURCES) $(PURESIGNAL_SOURCES) $(STEMLAB_SOURCES)
+all: prebuild $(PROGRAM) $(HEADERS) $(REMOTE_HEADERS) $(USBOZY_HEADERS) $(LIMESDR_HEADERS) $(FREEDV_HEADERS) $(LOCALCW_HEADERS) $(I2C_HEADERS) $(GPIO_HEADERS) $(PSK_HEADERS) $(PURESIGNAL_HEADERS) $(MIDI_HEADERS) $(STEMLAB_HEADERS) $(SOURCES) $(REMOTE_SOURCES) $(USBOZY_SOURCES) $(LIMESDR_SOURCES) $(FREEDV_SOURCES) $(I2C_SOURCES) $(GPIO_SOURCES) $(PSK_SOURCES) $(PURESIGNAL_SOURCES) $(MIDI_SOURCES) $(STEMLAB_SOURCES)
prebuild:
rm -f version.o
#############################################################################
app: $(OBJS) $(REMOTE_OBJS) $(USBOZY_OBJS) $(LIMESDR_OBJS) $(FREEDV_OBJS) \
$(LOCALCW_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(PSK_OBJS) \
- $(PURESIGNAL_OBJS) $(STEMLAB_OBJS)
+ $(PURESIGNAL_OBJS) $(MIDI_OBJS) $(STEMLAB_OBJS)
$(LINK) -headerpad_max_install_names -o $(PROGRAM) $(OBJS) $(REMOTE_OBJS) \
$(USBOZY_OBJS) $(I2C_OBJS) $(GPIO_OBJS) $(LIMESDR_OBJS) \
$(FREEDV_OBJS) $(LOCALCW_OBJS) $(PSK_OBJS) $(PURESIGNAL_OBJS) \
- $(STEMLAB_OBJS) $(LIBS)
+ $(MIDI_OBJS) $(STEMLAB_OBJS) $(LIBS)
@rm -rf pihpsdr.app
@mkdir -p pihpsdr.app/Contents/MacOS
@mkdir -p pihpsdr.app/Contents/Frameworks
install_name_tool -change "$$lib" "@executable_path/../Frameworks/$$libfn" pihpsdr.app/Contents/MacOS/pihpsdr-bin; \
done
#
-# Make "app" and copy wdspWisdom and props file to app bundle
+# Make "app" and copy local files app bundle
#
localapp: app
cp wdspWisdom pihpsdr.app/Contents/Resources
cp *.props pihpsdr.app/Contents/Resources
+ cp midi.inp pihpsdr.app/Contents/Resources
+ cp ip.addr pihpsdr.app/Contents/Resources
#############################################################################
}
int ext_vfo_filter_changed(void *data) {
- vfo_filter_changed((uintptr_t)data);
+ vfo_filter_changed((int)(uintptr_t)data);
return 0;
}
int ext_mode_update(void *data) {
start_mode();
- // DL1YCF added return statement
return 0;
}
int ext_filter_update(void *data) {
start_filter();
- // DL1YCF added return statement
return 0;
}
int ext_noise_update(void *data) {
start_noise();
- // DL1YCF added return statement
return 0;
}
int ext_ptt_update(void *data) {
- ptt_update((uintptr_t)data);
+ ptt_update((int)(uintptr_t)data);
return 0;
}
int ext_mox_update(void *data) {
- mox_update((uintptr_t)data);
+ mox_update((int)(uintptr_t)data);
return 0;
}
int ext_tune_update(void *data) {
- tune_update((uintptr_t)data);
+ tune_update((int)(uintptr_t)data);
return 0;
}
int ext_vox_changed(void *data) {
- vox_changed((uintptr_t)data);
+ vox_changed((int)(uintptr_t)data);
return 0;
}
}
int ext_vfo_band_changed(void *data) {
- vfo_band_changed((uintptr_t)data);
+ vfo_band_changed((int)(uintptr_t)data);
return 0;
}
int ext_radio_change_sample_rate(void *data) {
- radio_change_sample_rate((uintptr_t)data);
+ radio_change_sample_rate((int)(uintptr_t)data);
return 0;
}
int ext_update_squelch(void *data) {
- set_squelch(active_receiver);
+ set_squelch();
return 0;
}
vfo_step(step);
return 0;
}
+
+int ext_set_mic_gain(void * data) {
+ double d=*(double *)data;
+ set_mic_gain(d);
+ free(data);
+ return 0;
+}
+
+int ext_set_agc_gain(void *data) {
+ double d=*(double *)data;
+ set_agc_gain(d);
+ free(data);
+ return 0;
+}
+
+int ext_set_drive(void *data) {
+ double d=*(double *)data;
+ set_drive(d);
+ free(data);
+ return 0;
+}
+
+int ext_vfo_a_swap_b(void *data) {
+ vfo_a_swap_b();
+ return 0;
+}
+
+int ext_update_att_preamp(void *data) {
+ update_att_preamp();
+ return 0;
+}
+
+int ext_set_alex_attenuation(void *data) {
+ int val=(int)(uintptr_t)data;
+ set_alex_attenuation(val);
+ return 0;
+}
+
+int ext_set_attenuation_value(void *data) {
+ double d=*(double *)data;
+ set_attenuation_value(d);
+ free(data);
+ return 0;
+}
*/
+//
// The following calls functions can be called usig g_idle_add
+// Use these calls from within the rigclt daemon, or the GPIO or MIDI stuff
+//
extern int ext_discovery(void *data);
extern int ext_vfo_update(void *data);
extern int ext_tx_set_ps(void *data);
extern int ext_ps_twotone(void *data);
#endif
+
int ext_vfo_step(void *data);
int ext_vfo_mode_changed(void *data);
+int ext_set_af_gain(void *data);
+int ext_set_mic_gain(void *data);
+int ext_set_agc_gain(void *data);
+int ext_set_drive(void *data);
+int ext_vfo_a_swap_b(void *data);
+int ext_update_att_preamp(void *data);
+int ext_set_alex_attenuation(void *data);
+int ext_set_attenuation_value(void *data);
value=100.0;
}
active_receiver->squelch=value;
- set_squelch(active_receiver);
+ set_squelch();
break;
case ENCODER_COMP:
value=(double)transmitter->compressor_level;
* RF3 respects the "TX drive" and "TX ATT" settings
* RF4 is the TX signal multiplied with 0.4 (ignores TX_DRIVE and TX_ATT).
*
- * We support 4 receivers, they see
- * RX1: RF1
- * RX2: RF2 while receiving, RF3 while transmitting/48000
- * RX3: RF2 while receiving, RF3 while transmitting/48000
- * RX4: RF3
+ * Depending on the device type, the receivers see different signals
+ * (This is necessary for PURESIGNAL). We chose the association such that
+ * it works both with and without PURESIGNAL. Upon receiving, the association
+ * is as follows:
+ * RX1=RF1, RX2=RF2, RX3=RF2,
*
- * RX5 to RX8: no signal
- *
- * This is the setting for PURESIGNAL with HERMES boards.
- * To simulate Orion2 boards (Anan7000 etc), we should connect RX4 with RF2 and RX5 with RF3.
+ * The connection upon transmitting depends on the DEVICE, namely
+ *
+ * DEVICE=METIS: RX1=RF3, RX2=RF4
+ * DEVICE=HERMES: RX3=RF3, RX4=RF4 (also for DEVICE=StemLab)
+ * DEVICE=ORION2: RX4=RF3, RX5=RF5 (also for DEVICE=ANGELIA and ORION)
*
+ * Note that currently, RF3 and RF4 only exist when using 48000 Hz sample rate.
+ *
*/
#include <stdio.h>
#include <errno.h>
* Whenevery they are changed, this is reported.
*/
-int AlexTXrel = -1;
-int alexRXout = -1;
-int alexRXant = -1;
-int MicTS = -1;
-int duplex = -1;
-int receivers = -1;
-int rate = -1;
-int preamp = -1;
-int LTdither = -1;
-int LTrandom = -1;
-int AlexRXant = -1;
-int AlexRXout = -1;
-int ref10 = -1;
-int src122 = -1;
-int PMconfig = -1;
-int MicSrc = -1;
-int txdrive = 0;
-int txatt = 0;
-int sidetone_volume = -1;
-int cw_internal = -1;
-int rx_att[2] = {-1,-1};
-int rx1_attE = -1;
-int rx_preamp[4] = {-1,-1,-1,-1};
-int MerTxATT0 = -1;
-int MerTxATT1 = -1;
-int MetisDB9 = -1;
-int PeneSel = -1;
-int PureSignal = -1;
-int LineGain = -1;
-int MicPTT = -1;
-int tip_ring = -1;
-int MicBias = -1;
-int ptt=-1;
-int att=-1;
-int TX_class_E = -1;
-int OpenCollectorOutputs=-1;
-long tx_freq=-1;
-long rx_freq[7] = {-1,-1,-1,-1,-1,-1,-1};
-int hermes_config=-1;
-int alex_lpf=-1;
-int alex_hpf=-1;
-int c25_ext_board_i2c_data=-1;
-int rx_adc[7]={0,1,1,2,-1,-1,-1};
-int cw_hang = -1;
-int cw_reversed = -1;
-int cw_speed = -1;
-int cw_mode = -1;
-int cw_weight = -1;
-int cw_spacing = -1;
-int cw_delay = -1;
-int CommonMercuryFreq = -1;
-int freq=-1;
+static int AlexTXrel = -1;
+static int alexRXout = -1;
+static int alexRXant = -1;
+static int MicTS = -1;
+static int duplex = -1;
+static int receivers = -1;
+static int rate = -1;
+static int preamp = -1;
+static int LTdither = -1;
+static int LTrandom = -1;
+static int AlexRXant = -1;
+static int AlexRXout = -1;
+static int ref10 = -1;
+static int src122 = -1;
+static int PMconfig = -1;
+static int MicSrc = -1;
+static int txdrive = 0;
+static int txatt = 0;
+static int sidetone_volume = -1;
+static int cw_internal = -1;
+static int rx_att[2] = {-1,-1};
+static int rx1_attE = -1;
+static int rx_preamp[4] = {-1,-1,-1,-1};
+static int MerTxATT0 = -1;
+static int MerTxATT1 = -1;
+static int MetisDB9 = -1;
+static int PeneSel = -1;
+static int PureSignal = -1;
+static int LineGain = -1;
+static int MicPTT = -1;
+static int tip_ring = -1;
+static int MicBias = -1;
+static int ptt=-1;
+static int att=-1;
+static int TX_class_E = -1;
+static int OpenCollectorOutputs=-1;
+static long tx_freq=-1;
+static long rx_freq[7] = {-1,-1,-1,-1,-1,-1,-1};
+static int hermes_config=-1;
+static int alex_lpf=-1;
+static int alex_hpf=-1;
+static int c25_ext_board_i2c_data=-1;
+static int rx_adc[7]={0,1,1,2,-1,-1,-1};
+static int cw_hang = -1;
+static int cw_reversed = -1;
+static int cw_speed = -1;
+static int cw_mode = -1;
+static int cw_weight = -1;
+static int cw_spacing = -1;
+static int cw_delay = -1;
+static int CommonMercuryFreq = -1;
+static int freq=-1;
// floating-point represeners of TX att, RX att, and RX preamp settings
-double txdrv_dbl = 1.0;
-double txatt_dbl = 1.0;
-double rxatt_dbl[4] = {1.0, 1.0, 1.0, 1.0}; // this reflects both ATT and PREAMP
+static double txdrv_dbl = 1.0;
+static double txatt_dbl = 1.0;
+static double rxatt_dbl[4] = {1.0, 1.0, 1.0, 1.0}; // this reflects both ATT and PREAMP
-int sock_ep2;
+static int sock_ep2;
-struct sockaddr_in addr_ep6;
+static struct sockaddr_in addr_ep6;
/*
* These two variables monitor whether the TX thread is active
*/
-int enable_thread = 0;
-int active_thread = 0;
+static int enable_thread = 0;
+static int active_thread = 0;
void process_ep2(uint8_t *frame);
void *handler_ep6(void *arg);
// 63 * 130, RTXLEN must be an even multiple of 63!
#define RTXLEN 8190
-double isample[RTXLEN];
-double qsample[RTXLEN];
-int txptr=0;
-int rxptr=0;
+static double isample[RTXLEN];
+static double qsample[RTXLEN];
+static int txptr=0;
+static int rxptr=0;
+
+static int ismetis,isorion,ishermes,isc25;
int main(int argc, char *argv[])
{
struct sched_param param;
pthread_attr_t attr;
pthread_t thread;
+ int DEVICE;
uint8_t reply[11] = { 0xef, 0xfe, 2, 0, 0, 0, 0, 0, 0, 32, 1 };
int bytes_read, bytes_left;
uint32_t *code0 = (uint32_t *) buffer; // fast access to code of first buffer
+/*
+ * Examples for METIS: ANAN10E, ANAN100B
+ * Examples for HERMES: HERMES, ANAN10, ANAN100
+ * Examples for ORION: ANAN100D, ANAN200D
+ * Examples for ORION2: ANAN7000D, ANAN8000D
+ */
+
+ DEVICE=1; // default is Hermes
+ if (argc > 1) {
+ if (!strncmp(argv[1],"-metis" ,6)) DEVICE=0;
+ if (!strncmp(argv[1],"-hermes" ,7)) DEVICE=1;
+ if (!strncmp(argv[1],"-orion" , 6)) DEVICE=5;
+ if (!strncmp(argv[1],"-orion2" ,7)) DEVICE=10; // Anan7000 in old protocol
+ if (!strncmp(argv[1],"-c25" ,8)) DEVICE=100; // the same as hermes
+ }
+ ismetis=ishermes=isorion=isc25;
+ switch (DEVICE) {
+ case 0: fprintf(stderr,"DEVICE is METIS\n"); ismetis=1; break;
+ case 1: fprintf(stderr,"DEVICE is HERMES\n"); ishermes=1; break;
+ case 5: fprintf(stderr,"DEVICE is ORION\n"); isorion=1; break;
+ case 10: fprintf(stderr,"DEVICE is ORION2\n"); isorion=1; break;
+ case 100: fprintf(stderr,"DEVICE is StemLab\n"); ishermes=1; isc25=1; break;
+ }
+ reply[10]=DEVICE;
+
if ((sock_ep2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
perror("socket");
memset(buffer, 0, 60);
memcpy(buffer, reply, 11);
- // ab-use some of the "unused" bytes in the reply block to indicate we can do TCP
- // This information can be used by SDR programs
- buffer[11]='T';
- buffer[12]='C';
- buffer[13]='P';
if (sock_TCP_Client > -1)
{
// We will get into trouble if we respond via TCP while the radio is
chk_data((frame[3] & 0x03) >> 0, att, "ALEX Attenuator");
chk_data((frame[3] & 0x04) >> 2, preamp, "ALEX preamp");
- chk_data((frame[3] & 0x08) >> 2, LTdither, "LT2208 Dither");
- chk_data((frame[3] & 0x10) >> 2, LTrandom, "LT2208 Random");
+ chk_data((frame[3] & 0x08) >> 3, LTdither, "LT2208 Dither");
+ chk_data((frame[3] & 0x10) >> 4, LTrandom, "LT2208 Random");
chk_data((frame[3] & 0x60) >> 5, alexRXant, "ALEX RX ant");
chk_data((frame[3] & 0x80) >> 7, alexRXout, "ALEX RX out");
chk_data(((frame[4] >> 3) & 7) + 1, receivers, "RECEIVERS");
chk_data(((frame[4] >> 6) & 1), MicTS, "TimeStampMic");
chk_data(((frame[4] >> 7) & 1), CommonMercuryFreq,"Common Mercury Freq");
+
+ if (isc25) {
+ // Charly25: has two 18-dB preamps that are switched with "preamp" and "dither"
+ // and a step-attenuator triggered by Alex ATT ONLY RX1!
+ rxatt_dbl[0]=pow(10.0, -0.05*(12*att-18*LTdither-18*preamp));
+ rxatt_dbl[1]=1.0;
+ }
break;
case 2:
chk_data((frame[4] & 0x1F) >> 0, rx_att[0], "RX1 ATT");
chk_data((frame[4] & 0x20) >> 5, rx1_attE, "RX1 ATT enable");
- // Set RX amplification factors. Assume 20 dB preamps
- rxatt_dbl[0]=pow(10.0, -0.05*(rx_att[0]-20*rx_preamp[0]));
- rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1]-20*rx_preamp[1]));
- rxatt_dbl[2]=pow(10.0, (double) rx_preamp[2]);
- rxatt_dbl[3]=pow(10.0, (double) rx_preamp[3]);
+ if (!isc25) {
+ // Set RX amplification factors. Assume 20 dB preamps
+ rxatt_dbl[0]=pow(10.0, -0.05*(rx_att[0]-20*rx_preamp[0]));
+ rxatt_dbl[1]=pow(10.0, -0.05*(rx_att[1]-20*rx_preamp[1]));
+ rxatt_dbl[2]=pow(10.0, (double) rx_preamp[2]);
+ rxatt_dbl[3]=pow(10.0, (double) rx_preamp[3]);
+ }
break;
case 22:
127, 127, 127, 24, 0, 0, 0, 0,
127, 127, 127, 32, 66, 66, 66, 66
};
- int32_t sample;
+ int32_t rf1isample,rf1qsample;
+ int32_t rf2isample,rf2qsample;
+ int32_t rf3isample,rf3qsample;
+ int32_t rf4isample,rf4qsample;
+
+ int32_t myisample,myqsample;
+
struct timespec delay;
#ifdef __APPLE__
struct timespec now;
pointer += 8;
memset(pointer, 0, 504);
for (j=0; j<n; j++) {
+ //
+ // Define samples of our sources RF1 through RF4
+ //
+ rf1isample= noiseItab[noiseIQpt] * 8388607.0; // Noise
+ rf1isample += T0800Itab[pt0800] * 83.886070 *rxatt_dbl[0]; // tone 100 dB below peak
+ rf1qsample=noiseQtab[noiseIQpt] * 8388607.0;
+ rf1qsample += T0800Qtab[pt0800] * 83.886070 *rxatt_dbl[0];
+ //
+ rf2isample= noiseItab[noiseIQpt] * 8388607.0; // Noise
+ rf2isample += T2000Itab[pt2000] * 838.86070 * rxatt_dbl[1]; // tone 80 dB below peak
+ rf2qsample=noiseQtab[noiseIQpt] * 8388607.0;
+ rf2qsample += T2000Qtab[pt2000] * 838.86070 * rxatt_dbl[1];
+ //
+ // RF3: TX signal distorted
+ //
+ i1=isample[rxptr]*txdrv_dbl;
+ q1=qsample[rxptr]*txdrv_dbl;
+ fac=IM3a+IM3b*(i1*i1+q1*q1);
+ rf3isample= txatt_dbl*i1*fac * 8388607.0;
+ rf3qsample= txatt_dbl*q1*fac * 8388607.0;
+ //
+ // RF4: TX signal with peak=0.4
+ //
+ rf4isample= isample[rxptr] * 0.400 * 8388607.0;
+ rf4qsample= qsample[rxptr] * 0.400 * 8388607.0;
+
+
+
for (k=0; k< receivers; k++) {
+ myisample=0;
+ myqsample=0;
switch (k) {
- case 0: // RX1 sees RF1
- //
- // RF1: noise + weak 800 Hz tone
- //
- sample= noiseItab[noiseIQpt] * 8388607.0;
- sample += T0800Itab[pt0800] * 83.886070 *rxatt_dbl[0]; // 100 dB below peak
- *pointer++ = (sample >> 16) & 0xFF;
- *pointer++ = (sample >> 8) & 0xFF;
- *pointer++ = (sample >> 0) & 0xFF;
- sample=noiseQtab[noiseIQpt] * 8388607.0;
- sample += T0800Qtab[pt0800] * 83.886070 *rxatt_dbl[0]; // 100 dB below peak
- *pointer++ = (sample >> 16) & 0xFF;
- *pointer++ = (sample >> 8) & 0xFF;
- *pointer++ = (sample >> 0) & 0xFF;
- break;
- case 1: // RX2 and RX3 see RF2 upon receiving, RF3 upon transmitting
- case 2:
- if (rate == 0 && ptt) {
- //
- // RF3:
- // Distorted (feed-back) TX signal
- // Note we first add distortion, then adjust level
- // Therefore we first multiply with txdrv_dbl, then
- // distort, and then attenuate with txatt_dbl.
- //
- i1=isample[rxptr]*txdrv_dbl;
- q1=qsample[rxptr]*txdrv_dbl;
- fac=IM3a+IM3b*(i1*i1+q1*q1);
- sample= txatt_dbl*i1*fac * 8388607.0;
- *pointer++ = (sample >> 16) & 0xFF;
- *pointer++ = (sample >> 8) & 0xFF;
- *pointer++ = (sample >> 0) & 0xFF;
- sample= txatt_dbl*q1*fac * 8388607.0;
- *pointer++ = (sample >> 16) & 0xFF;
- *pointer++ = (sample >> 8) & 0xFF;
- *pointer++ = (sample >> 0) & 0xFF;
- } else {
- //
- // RF2: noise + weak 2000 Hz tone
- //
- sample= noiseItab[noiseIQpt] * 8388607.0;
- sample += T2000Itab[pt2000] * 838.86070 * rxatt_dbl[1]; // 80 dB below peak
- *pointer++ = (sample >> 16) & 0xFF;
- *pointer++ = (sample >> 8) & 0xFF;
- *pointer++ = (sample >> 0) & 0xFF;
- sample=noiseQtab[noiseIQpt] * 8388607.0;
- sample += T2000Qtab[pt2000] * 838.86070 * rxatt_dbl[1]; // 80 dB below peak
- *pointer++ = (sample >> 16) & 0xFF;
- *pointer++ = (sample >> 8) & 0xFF;
- *pointer++ = (sample >> 0) & 0xFF;
- }
- break;
- case 3: // RX4 sees TX outgoing signal (no distortion, no attenuation)
- //
- // RF4: TX signal with HWPeak = 0.4
- //
- if (rate == 0 && ptt) {
- sample= isample[rxptr] * 0.400 * 8388607.0;
- *pointer++ = (sample >> 16) & 0xFF;
- *pointer++ = (sample >> 8) & 0xFF;
- *pointer++ = (sample >> 0) & 0xFF;
- sample= qsample[rxptr] * 0.400 * 8388607.0;
- *pointer++ = (sample >> 16) & 0xFF;
- *pointer++ = (sample >> 8) & 0xFF;
- *pointer++ = (sample >> 0) & 0xFF;
- } else {
- pointer +=6;
- }
- break;
- default:
- pointer +=6;
+ case 0: // RX1
+ if (rate == 0 && ptt && ismetis) {
+ myisample=rf3isample;
+ myqsample=rf3qsample;
+ } else {
+ myisample=rf1isample;
+ myqsample=rf1qsample;
+ }
+ break;
+ case 1: // RX2
+ if (rate == 0 && ptt && ismetis) {
+ myisample=rf4isample;
+ myqsample=rf4qsample;
+ } else {
+ myisample=rf2isample;
+ myqsample=rf2qsample;
+ }
+ break;
+ case 2:
+ if (rate == 0 && ptt && ishermes) {
+ myisample=rf3isample;
+ myqsample=rf3qsample;
+ } else {
+ myisample=rf2isample;
+ myqsample=rf2qsample;
+ }
+ break;
+ case 3: // RX4
+ if (rate == 0 && ptt && ishermes) {
+ myisample=rf4isample;
+ myqsample=rf4qsample;
+ } else if (rate == 0 && ptt && isorion) {
+ myisample=rf3isample;
+ myqsample=rf3qsample;
+ }
+ break;
+ case 4: // RX5
+ if (rate == 0 && ptt && isorion) {
+ myisample=rf4isample;
+ myqsample=rf4qsample;
+ }
+ break;
}
+ *pointer++ = (myisample >> 16) & 0xFF;
+ *pointer++ = (myisample >> 8) & 0xFF;
+ *pointer++ = (myisample >> 0) & 0xFF;
+ *pointer++ = (myqsample >> 16) & 0xFF;
+ *pointer++ = (myqsample >> 8) & 0xFF;
+ *pointer++ = (myqsample >> 0) & 0xFF;
}
// Microphone samples: silence
pointer += 2;
--- /dev/null
+/*
+ * MIDI support for pihpsdr
+ * (C) Christoph van Wullen, DL1YCF.
+ *
+ * This is the "Layer-1" for Apple Macintosh.
+ *
+ * This file implements the function register_midi_device,
+ * which is called with the name of a supported MIDI device and
+ * the number of characters in the name that must be equal to
+ * the name of the MIDI device as known in the operating system.
+ *
+ * For example, the call
+ * register_midi_device("COMPANY MIDI X",13)
+ * will also use a MIDI device named "COMPANY MIDI Y".
+ *
+ * This file must generate calls to Layer-2 NewMidiEvent().
+ * Some type of messages are not consideres (pressure change, etc.),
+ * they are silently dropped but must be processed.
+ *
+ */
+
+#include "midi.h"
+
+#ifdef __APPLE__
+
+#include <Carbon/Carbon.h>
+
+#include <CoreMIDI/MIDIServices.h>
+#include <CoreAudio/HostTime.h>
+#include <CoreAudio/CoreAudio.h>
+
+//
+// MIDI callback function
+//
+static void ReadMIDIdevice(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {
+ int i,j,k,command,chan;
+ MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
+
+
+ // loop through all packets in the current list
+ for (j=0; j < pktlist->numPackets; ++j) {
+ if (packet->length > 0) {
+ for ( i = 0; i<(packet->length); ) {
+ command=packet->data[i];
+ if ((command & 128) != 128) continue;
+
+ chan = command & 0x0F;
+
+ switch (command & 0xF0) {
+ case 0x80: // Note off
+ NewMidiEvent(MIDI_NOTE, chan, packet->data[i+1], 0);
+ fprintf(stderr,"NOTE OFF: Note=%d Chan=%d Vel=%d\n", packet->data[i+1], chan, packet->data[i+2]);
+ i +=3;
+ break;
+ case 0x90: // Note on
+ NewMidiEvent(MIDI_NOTE, chan, packet->data[i+1], 1);
+ fprintf(stderr,"NOTE ON : Note=%d Chan=%d Vel=%d\n", packet->data[i+1], chan, packet->data[i+2]);
+ i +=3;
+ break;
+ case 0xA0: // Polyph. Press.
+ fprintf(stderr,"PolPress: Note=%d Chan=%d Prs=%d\n", packet->data[i+1], chan, packet->data[i+2]);
+ i +=3;
+ break;
+ case 0xB0: // Control change
+ NewMidiEvent(MIDI_CTRL, chan, packet->data[i+1], packet->data[i+2]);
+ fprintf(stderr,"CtlChang: Ctrl=%d Chan=%d Val=%d\n", packet->data[i+1], chan, packet->data[i+2]);
+ i +=3;
+ break;
+ case 0xC0: // Program change
+ fprintf(stderr,"PgmChang: Prog=%d Chan=%d\n", packet->data[i+1], chan);
+ i +=2;
+ break;
+ case 0xD0: // Channel Pressure
+ fprintf(stderr, "ChanPres: Pres=%d Chan=%d\n", packet->data[i+1], chan);
+ i +=2;
+ break;
+ case 0xE0: // Pitch Bend
+ NewMidiEvent(MIDI_PITCH, chan, 0, packet->data[i+1] + 128*packet->data[i+2]);
+ fprintf(stderr,"Pitch : val =%d Chan=%d\n", packet->data[i+1] + 128*packet->data[i+2], chan);
+ i +=3;
+ break;
+ case 0xF0:
+ fprintf(stderr, "System : %x", command);
+ while ((command = (packet->data[++i]) & 128) != 128) {
+ fprintf(stderr," %x",command);
+ }
+ fprintf(stderr,"\n");
+ break;
+ }
+ } // i-loop through the packet
+ packet = MIDIPacketNext(packet);
+ } // if packet length > 1
+ } // j-loop through the list of packets
+}
+
+
+void register_midi_device(char *myname) {
+ unsigned long nDevices;
+ int i;
+ CFStringRef pname;
+ char name[100];
+ int FoundMIDIref=-1;
+ int mylen=strlen(myname);
+
+
+//
+// Go through the list of MIDI devices and
+// look whether the one we are looking for is there
+//
+
+ nDevices=MIDIGetNumberOfSources();
+ for (i=0; i<nDevices; i++) {
+ MIDIEndpointRef dev = MIDIGetSource(i);
+ if (dev != 0) {
+ MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &pname);
+ CFStringGetCString(pname, name, sizeof(name), 0);
+ CFRelease(pname);
+ fprintf(stderr,"MIDI device found: >>>%s<<<\n", name);
+ if (!strncmp(name, myname, mylen)) {
+ FoundMIDIref=i;
+ fprintf(stderr,"MIDI device found and selected: >>>%s<<<\n", name);
+ } else {
+ fprintf(stderr,"MIDI device we were looking for : >>>%s<<<\n", myname);
+ fprintf(stderr,"MIDI device found BUT NOT SELECTED: >>>%s<<<\n", name);
+ }
+ }
+ }
+
+//
+// If we found "our" device, register a callback routine
+//
+
+ if (FoundMIDIref >= 0) {
+ MIDIClientRef client = 0;
+ MIDIPortRef myMIDIport = 0;
+ //Create client
+ MIDIClientCreate(CFSTR("piHPSDR"),NULL,NULL, &client);
+ MIDIInputPortCreate(client, CFSTR("FromMIDI"), ReadMIDIdevice, NULL, &myMIDIport);
+ MIDIPortConnectSource(myMIDIport,MIDIGetSource(FoundMIDIref), NULL);
+ }
+}
+#endif
--- /dev/null
+/*
+ * MIDI support for pihpsdr
+ *
+ * (C) Christoph van Wullen, DL1YCF.
+ *
+ * Midi support works in three layers
+ *
+ * Layer-1: hardware specific
+ * --------------------------
+ *
+ * Layer1 either implements a callback function (if the operating system
+ * supports MIDI) or a separate thread polling MIDI data. Whenever a
+ * MIDI command arrives, such as Note on/off or Midi-Controller value
+ * changed, it calls Layer 2.
+ *
+ * Layer-2: MIDI device specific
+ * -----------------------------
+ *
+ * Layer2 translates MIDI commands into pihpsdr actions. This is done with
+ * a table-driven algorithm, such that the same translator can be used for
+ * any MIDI device provided the tables have been set up correctly.
+ * It seems overly complicated to create a user interface for setting up
+ * these tables, instead a standard text file describing the MIDI device
+ * is read and the tables are set up.
+ * Layer-2 has SDR applications in mind, but is not necessarily specific
+ * to pihpsr. It calls the Layer-3 function.
+ *
+ * Layer-3: pihpsdr specific
+ * -------------------------
+ *
+ * Layer 3, finally, implements all the "actions" we can make, such as TUNE
+ * or VFO. This Layer calls pihpsdr functions.
+ *
+ * One word to MIDI channels. Usually, a MIDI device can be configured to use
+ * a specific channel, such that different keyboards use different channels.
+ * The Layer-2 tables can either specify that the MIDI command has to come from
+ * a specific channel, or can specify that the action will be taken not matter which
+ * channel the MIDI message comes from. The latter case should be the default, but
+ * if we want to connect more than one MIDI device, we need to speficy the channel.
+ *
+ * In principle this supports more than one MIDI device, but in this case they
+ * must generate MIDI events on different channels
+ */
+
+//
+// MIDIaction encodes the "action" to be taken in Layer3
+//
+enum MIDIaction {
+ ACTION_NONE=0,
+ VFO,
+ TUNE,
+ MOX,
+ AF_GAIN,
+ MIC_VOLUME,
+ TX_DRIVE,
+ ATT,
+ PRE,
+ AGC,
+ COMPRESS,
+ RIT_ONOFF,
+ RIT_VAL,
+ PAN_HIGH,
+ PAN_LOW,
+ BAND_UP,
+ BAND_DOWN,
+ FILTER_UP,
+ FILTER_DOWN,
+ MODE_UP,
+ MODE_DOWN,
+ SWAP_VFO
+};
+
+//
+// MIDItype encodes the type of MIDI control. This info
+// is passed from Layer-2 to Layer-3
+//
+// MIDI_KEY has no parameters and indicates that some
+// button has been pressed.
+//
+// MIDI_KNOB has a "value" parameter (between 0 and 100)
+// and indicates that some knob has been set to a specific
+// position.
+//
+// MIDI_WHEEL has a "direction" parameter and indicates that
+// some knob has been turned left/down or right/ip. The value
+// can be
+//
+// -100 very fast going down
+// -10 fast going down
+// -1 going down
+// 1 going up
+// 10 fast going up
+// 100 very fast going up
+//
+
+enum MIDItype {
+ TYPE_NONE=0,
+ MIDI_KEY, // Button (press event)
+ MIDI_KNOB, // Knob (value between 0 and 100)
+ MIDI_WHEEL // Wheel (direction and speed)
+};
+
+//
+// MIDIevent encodes the actual MIDI event "seen" in Layer-1 and
+// passed to Layer-2. MIDI_NOTE events end up as MIDI_KEY and
+// MIDI_PITCH as MIDI_KNOB, while MIDI_CTRL can end up both as
+// MIDI_KNOB or MIDI_WHEEL, depending on the device description.
+//
+enum MIDIevent {
+ EVENT_NONE=0,
+ MIDI_NOTE,
+ MIDI_CTRL,
+ MIDI_PITCH
+};
+
+//
+// Data structure for Layer-2
+//
+
+//
+// There is linked list of all specified MIDI events for a given "Note" value,
+// which contains the defined actions for all MIDI_NOTE and MIDI_CTRL events
+// with that given note and for all channels
+// Note on wheel delay:
+// If using a wheel for cycling through a menu, it is difficult to "hit" the correct
+// menu item if wheel events are generated at a very high rate. Therefore we can define
+// a delay: once a wheel event is reported upstream, any such events are suppressed during
+// the delay.
+//
+// Note that with a MIDI KEY, you can choose that an action is
+// generated only for a NOTE_ON event or both for NOTE_ON and
+// NOTE_OFF. In the first case, if the key is associated to MOX,
+// then MOX is toggled each time the key is pressed. This behaves
+// very much like point-and-clicking the MOX buttion in the GUI.
+//
+// If an action is generated both on NOTE_ON and NOTE_OFF,
+// then MOX is engaged when pressing the key and disengaged
+// when releasing it. For MOX this makes little send but you
+// might want to configure the TUNE button this way.
+// The latter behaviour is triggered when the line assigning the key
+// or "NOTE OFF". The table speficying the behaviour of layer-2 thus
+// contains the key word "ONOFF". This is stored in the field "onoff"
+// in struct desc.
+
+struct desc {
+ int channel; // -1 for ANY channel
+ enum MIDIevent event; // type of event (NOTE on/off, Controller change, Pitch value)
+ int onoff; // 1: generate upstream event both for Note-on and Note-off
+ enum MIDItype type; // Key, Knob, or Wheel
+ int low_thr3; // Wheel only: If controller value is <= this value, generate "very fast down"
+ int low_thr2; // Wheel only: If controller value is <= this value, generate " fast down"
+ int low_thr1; // Wheel only: If controller value is <= this value, generate " down"
+ int up_thr1; // Wheel only: If controller value is <= this value, generate " up "
+ int up_thr2; // Wheel only: If controller value is <= this value, generate " fast up "
+ int up_thr3; // Wheel only: If controller value is <= this value, generate "very fast up "
+ int delay; // Wheel only: delay (msec)
+ enum MIDIaction action; // SDR "action" to generate
+ struct desc *next; // Next defined action for a controller/key with that note value.
+};
+
+struct {
+ struct desc *desc[128]; // description for Note On/Off and ControllerChange
+ struct desc *pitch; // description for PitchChanges
+} MidiCommandsTable;
+
+//
+// Layer-1 entry point, called once for all the MIDI devices
+// that have been defined. This is called upon startup by
+// Layer-2 through the function MIDIstartup.
+//
+void register_midi_device(char *name);
+
+//
+// Layer-2 entry point (called by Layer1)
+//
+// When Layer-1 has received a MIDI message, it calls
+// NewMidiEvent.
+//
+// MIDIstartup looks for files containing descriptions for MIDI
+// devices and calls the Layer-1 function register_midi_device
+// for each device description that was successfully read.
+
+void NewMidiEvent(enum MIDIevent event, int channel, int note, int val);
+void MIDIstartup();
+
+//
+// Layer-3 entry point (called by Layer2). In Layer-3, all the pihpsdr
+// actions (such as changing the VFO frequency) are performed.
+// The implementation of DoTheMIDI is tightly bound to pihpsr.
+//
+
+void DoTheMidi(enum MIDIaction code, enum MIDItype type, int val);
--- /dev/null
+#
+# Sample midi.inp file, suitable for a Behringer CMD PL-1 MIDI controller
+#
+# Note that the Attenuator is implemented twice, as a key and as a wheel
+# The key is suitable for radios with a step (ALEX) attenuator, the wheel
+# fits best for radios with a programmable attenuator (0-31 dB)
+#
+DEVICE=CMD PL 1
+CTRL=31 WHEEL THR=59 61 63 65 67 69 ACTION=VFO # Big wheel: : main VFO knob
+PITCH ACTION=AFGAIN # Big slider : AF gain
+KEY=16 ACTION=PREAMP # Key 1 : Cycle through Preamp settings
+KEY=17 ACTION=ATT # Key 2 : Cycle through ATT (Alex ATT) settings
+KEY=18 ACTION=RITTOGGLE # Key 3 : RIT on/off
+KEY=19 ACTION=NONE # Key 4 :
+KEY=20 ACTION=NONE # Key 5 :
+KEY=21 ACTION=NONE # Key 6 :
+KEY=22 ACTION=NONE # Key 7 :
+KEY=23 ACTION=NONE # Key 8 :
+KEY=24 ACTION=TUNE # LOAD button: TUNE on/off
+KEY=27 ACTION=SWAPVFO # SCRATCH button: Swap VFOs A and B
+KEY=34 ACTION=MOX # CUE button: MOX on/off
+KEY=36 ACTION=MODEDOWN # << button: Mode down
+KEY=37 ACTION=MODEUP # >> button: Mode up
+KEY=38 ACTION=BANDDOWN # - button: Band down
+KEY=39 ACTION=BANDUP # + button: Band up
+CTRL=0 WHEEL THR=-1 -1 63 65 128 128 ACTION=ATT # Knob 1 : RX att
+CTRL=1 WHEEL THR=-1 -1 63 65 128 128 ACTION=COMPRESS # Knob 2 : TX compression
+CTRL=2 WHEEL THR=-1 -1 63 65 128 128 ACTION=RITVAL # Knob 3 : RIT value
+CTRL=3 WHEEL THR=-1 -1 63 65 128 128 ACTION=PANLOW # Knob 4 : Panadapter low
+CTRL=4 WHEEL THR=-1 -1 63 65 128 128 ACTION=AGC # Knob 5 : AGC
+CTRL=5 WHEEL THR=-1 -1 63 65 128 128 ACTION=MICGAIN # Knob 6 : MIC gain
+CTRL=6 WHEEL THR=-1 -1 63 65 128 128 ACTION=RFPOWER # Knob 7 : TX drive
+CTRL=7 WHEEL THR=-1 -1 63 65 128 128 ACTION=FILTERUP # Knob 8 : cycle through the filters
--- /dev/null
+/*
+ * Layer-2 of MIDI support
+ *
+ * (C) Christoph van Wullen, DL1YCF
+ *
+ * Using the data in MIDICommandsTable, this subroutine translates the low-level
+ * MIDI events into MIDI actions in the SDR console.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include "midi.h"
+
+void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) {
+
+ struct desc *desc;
+ int new;
+ static enum MIDIaction last_wheel_action;
+ static struct timespec tp, last_wheel_tp={0,0};
+ long delta;
+
+ if (event == MIDI_PITCH) {
+ desc=MidiCommandsTable.pitch;
+ } else {
+ desc=MidiCommandsTable.desc[note];
+ }
+ while (desc) {
+ if ((desc->channel == channel || desc->channel == -1) && (desc->event == event)) {
+ // Found matching entry
+ switch (desc->event) {
+ case MIDI_NOTE:
+ if ((val == 1 || (val == 0 && desc->onoff == 1)) && desc->type == MIDI_KEY) {
+ DoTheMidi(desc->action, desc->type, 0);
+ }
+ break;
+ case MIDI_CTRL:
+ if (desc->type == MIDI_KNOB) {
+ // normalize value to range 0 - 100
+ new = (val*100)/127;
+ DoTheMidi(desc->action, desc->type, new);
+ } else if (desc->type == MIDI_WHEEL) {
+ if (desc->delay > 0) {
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ delta=1000*(tp.tv_sec - last_wheel_tp.tv_sec);
+ delta += (tp.tv_nsec - last_wheel_tp.tv_nsec)/1000000;
+ if (delta < desc->delay) break;
+ last_wheel_tp = tp;
+ }
+ // translate value to direction
+ if (val <= desc->low_thr1) new=-1;
+ if (val <= desc->low_thr2) new=-10;
+ if (val <= desc->low_thr3) new=-100;
+ if (val >= desc->up_thr1 ) new=1;
+ if (val >= desc->up_thr2 ) new=10;
+ if (val >= desc->up_thr3 ) new=100;
+ DoTheMidi(desc->action, desc->type, new);
+ last_wheel_action=desc->action;
+ }
+ break;
+ case MIDI_PITCH:
+ if (desc->type == MIDI_KNOB) {
+ // normalize value to 0 - 100
+ new = (val*100)/16383;
+ fprintf(stderr,"PITCH calc: val=%d new=%d\n", val,new);
+ DoTheMidi(desc->action, desc->type, new);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ } else {
+ desc=desc->next;
+ }
+ }
+}
+
+/*
+ * This data structre connects names as used in the midi.inp file with
+ * our MIDIaction enum values
+ */
+
+static struct {
+ enum MIDIaction action;
+ const char *str;
+} ActionTable[] = {
+ { VFO, "VFO"},
+ { TUNE, "TUNE"},
+ { MOX, "MOX"},
+ { AF_GAIN, "AFGAIN"},
+ { MIC_VOLUME, "MICGAIN"},
+ { TX_DRIVE, "RFPOWER"},
+ { ATT, "ATT"},
+ { PRE, "PREAMP"},
+ { AGC, "AGC"},
+ { COMPRESS, "COMPRESS"},
+ { RIT_ONOFF, "RITTOGGLE"},
+ { RIT_VAL, "RITVAL"},
+ { PAN_HIGH, "PANHIGH"},
+ { PAN_LOW, "PANLOW"},
+ { BAND_UP, "BANDUP"},
+ { BAND_DOWN, "BANDDOWN"},
+ { FILTER_UP, "FILTERUP"},
+ { FILTER_DOWN, "FILTERDOWN"},
+ { MODE_UP, "MODEUP"},
+ { MODE_DOWN, "MODEDOWN"},
+ { SWAP_VFO, "SWAPVFO"},
+ { ACTION_NONE, NULL}
+};
+
+/*
+ * Translation from keyword in midi.inp file to MIDIaction
+ */
+
+static enum MIDIaction keyword2action(char *s) {
+ int i=0;
+
+ for (i=0; 1; i++) {
+ if (ActionTable[i].str == NULL) return ACTION_NONE;
+ if (!strcmp(s, ActionTable[i].str)) return ActionTable[i].action;
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Here we read in a MIDI description file "midi.def" and fill the MidiCommandsTable
+ * data structure
+ */
+
+void MIDIstartup() {
+ FILE *fpin,*fpout;
+ char zeile[255];
+ char *cp,*cq;
+ int key;
+ enum MIDIaction action;
+ int chan;
+ int is_wheel;
+ int lt3,lt2,lt1,ut1,ut2,ut3;
+ int onoff, delay;
+ struct desc *desc,*dp;
+ enum MIDItype type;
+ enum MIDIevent event;
+ int i;
+
+ for (i=0; i<128; i++) MidiCommandsTable.desc[i]=NULL;
+ MidiCommandsTable.pitch=NULL;
+
+ fpin=fopen("midi.inp", "r");
+ fpout=stderr;
+ if (!fpin) return;
+
+ for (;;) {
+ if (fgets(zeile, 255, fpin) == NULL) break;
+
+ // ignore comments
+ cp=index(zeile,'#');
+ if (cp == zeile) continue; // comment line
+ if (cp) *cp=0; // ignore trailing comment
+
+ if ((cp = strstr(zeile, "DEVICE="))) {
+ // Delete trailing blanks and newlines
+ cq=cp+7;
+ while (*cq != 0 && *cq != '\n' && *cq != ' ' && *cq != '\t') cq++;
+ *cq=0;
+ register_midi_device(cp+7);
+ continue; // nothing more in this line
+ }
+ chan=-1; // default: any channel
+ lt3=lt2=lt1=0;
+ ut3=ut2=ut1=127;
+ onoff=0;
+ event=EVENT_NONE;
+ type=TYPE_NONE;
+ key=0;
+ delay=0;
+
+ if ((cp = strstr(zeile, "KEY="))) {
+ sscanf(cp+4, "%d", &key);
+ event=MIDI_NOTE;
+ type=MIDI_KEY;
+ }
+ if ((cp = strstr(zeile, "CTRL="))) {
+ sscanf(cp+5, "%d", &key);
+ event=MIDI_CTRL;
+ type=MIDI_KNOB;
+ }
+ if ((cp = strstr(zeile, "PITCH "))) {
+ event=MIDI_PITCH;
+ type=MIDI_KNOB;
+ }
+ if ((cp = strstr(zeile, "CHAN="))) {
+ sscanf(cp+5, "%d", &chan);
+ chan--;
+ if (chan<0 || chan>15) chan=-1;
+ }
+ if ((cp = strstr(zeile, "WHEEL"))) {
+ // change type from MIDI_KNOB to MIDI_WHEEL
+ type=MIDI_WHEEL;
+ }
+ if ((cp = strstr(zeile, "ONOFF"))) {
+ onoff=1;
+ }
+ if ((cp = strstr(zeile, "DELAY="))) {
+ sscanf(cp+6, "%d", &delay);
+ }
+ if ((cp = strstr(zeile, "THR="))) {
+ sscanf(cp+4, "%d %d %d %d %d %d", <3, <2, <1, &ut1, &ut2, &ut3);
+ is_wheel=1;
+ }
+ if ((cp = strstr(zeile, "ACTION="))) {
+ // cut zeile at the first blank character following
+ cq=cp+7;
+ while (*cq != 0 && *cq != '\n' && *cq != ' ' && *cq != '\t') cq++;
+ *cq=0;
+ action=keyword2action(cp+7);
+ }
+ if (event == EVENT_NONE || type == TYPE_NONE || key < 0 || key > 127) continue;
+ // Now all entries of the line have been read. Construct descriptor
+ fprintf(fpout,"K=%d C=%d T=%d E=%d A=%d OnOff=%d THRs=%d %d %d %d %d %d\n",
+ key,chan,type, event, action, onoff, lt3,lt2,lt1,ut1,ut2,ut3);
+ desc = (struct desc *) malloc(sizeof(struct desc));
+ desc->next = NULL;
+ desc->action = action;
+ desc->type = type;
+ desc->event = event;
+ desc->onoff = onoff;
+ desc->delay = delay;
+ desc->low_thr3 = lt3;
+ desc->low_thr2 = lt2;
+ desc->low_thr1 = lt1;
+ desc->up_thr1 = ut1;
+ desc->up_thr2 = ut2;
+ desc->up_thr3 = ut3;
+ desc->channel = chan;
+ // insert descriptor
+ if (event == MIDI_PITCH) {
+ dp = MidiCommandsTable.pitch;
+ if (dp == NULL) {
+ MidiCommandsTable.pitch = desc;
+ } else {
+ while (dp->next != NULL) dp=dp->next;
+ dp->next=desc;
+ }
+ }
+ if (event == MIDI_KEY || event == MIDI_CTRL) {
+ dp = MidiCommandsTable.desc[key];
+ if (dp == NULL) {
+ MidiCommandsTable.desc[key]=desc;
+ } else {
+ while (dp->next != NULL) dp=dp->next;
+ dp->next=desc;
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Layer-3 of MIDI support
+ *
+ * (C) Christoph van Wullen, DL1YCF
+ *
+ *
+ * In most cases, a certain action only makes sense for a specific
+ * type. For example, changing the VFO frequency will only be implemeted
+ * for MIDI_WHEEL, and TUNE off/on only with MIDI_KNOB.
+ *
+ * However, changing the volume makes sense both with MIDI_KNOB and MIDI_WHEEL.
+ */
+#include "radio.h"
+#include "vfo.h"
+#include "filter.h"
+#include "band.h"
+#include "mode.h"
+#include "new_menu.h"
+#include "sliders.h"
+#include "ext.h"
+#include "midi.h"
+
+void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
+
+ int new;
+ double dnew;
+ double *dp;
+
+ switch (action) {
+ case SWAP_VFO: // only key supported
+ if (type == MIDI_KEY) {
+ g_idle_add(ext_vfo_a_swap_b,NULL);
+ }
+ break;
+ case VFO: // only wheel supported
+ if (type == MIDI_WHEEL) g_idle_add(ext_vfo_step, (gpointer)(uintptr_t) val);
+ break;
+ case TUNE: // only key supported
+ if (type == MIDI_KEY) {
+ new = !tune;
+ g_idle_add(ext_tune_update, (gpointer)(long) new);
+ }
+ break;
+ case MOX: // only key supported
+ if (type == MIDI_KEY) {
+ new = !mox;
+ g_idle_add(ext_mox_update, (gpointer)(long) new);
+ }
+ break;
+ case AF_GAIN: // knob or wheel supported
+ if (type == MIDI_KNOB) {
+ active_receiver->volume = 0.01*val;
+ } else if (type == MIDI_WHEEL) {
+ dnew=active_receiver->volume += 0.01*val;
+ if (dnew < 0.0) dnew=0.0; if (dnew > 1.0) dnew=1.0;
+ active_receiver->volume = dnew;
+ } else {
+ break;
+ }
+ g_idle_add(ext_update_af_gain, NULL);
+ break;
+ case MIC_VOLUME: // knob or wheel supported
+ if (type == MIDI_KNOB) {
+ dnew=-10.0 + 0.6*val;
+ } else if (type == MIDI_WHEEL) {
+ dnew = mic_gain + val;
+ if (dnew < -10.0) dnew=-10.0; if (dnew > 50.0) dnew=50.0;
+ } else {
+ break;
+ }
+ dp=malloc(sizeof(double));
+ *dp=dnew;
+ g_idle_add(ext_set_mic_gain, (gpointer) dp);
+ break;
+ case AGC: // knob or wheel supported
+ if (type == MIDI_KNOB) {
+ dnew = -20.0 + 1.4*val;
+ } else if (type == MIDI_WHEEL) {
+ dnew=active_receiver->agc_gain + val;
+ if (dnew < -20.0) dnew=-20.0; if (dnew > 120.0) dnew=120.0;
+ } else {
+ break;
+ }
+ dp=malloc(sizeof(double));
+ *dp=dnew;
+ g_idle_add(ext_set_agc_gain, (gpointer) dp);
+ break;
+ case TX_DRIVE: // knob or wheel supported
+ if (type == MIDI_KNOB) {
+ dnew = val;
+ } else if (type == MIDI_WHEEL) {
+ dnew=transmitter->drive + val;
+ if (dnew < 0.0) dnew=0.0; if (dnew > 100.0) dnew=100.0;
+ } else {
+ break;
+ }
+ dp=malloc(sizeof(double));
+ *dp=dnew;
+ g_idle_add(ext_set_drive, (gpointer) dp);
+ break;
+ case BAND_UP: // key or wheel supported
+ case BAND_DOWN: // key or wheel supported
+ if (type == MIDI_KEY) {
+ new=(action == BAND_UP) ? 1 : -1;
+ } else if (type == MIDI_WHEEL) {
+ new=val;
+ } else {
+ break;
+ }
+ new+=vfo[active_receiver->id].band;
+ if (new >= BANDS) new=0;
+ if (new < 0) new=BANDS-1;
+ g_idle_add(ext_vfo_band_changed, (gpointer) (uintptr_t) new);
+ break;
+ case FILTER_UP: // key or wheel supported
+ case FILTER_DOWN: // key or wheel supported
+ if (type == MIDI_KEY) {
+ new=(action == FILTER_UP) ? 1 : -1;
+ } else if (type == MIDI_WHEEL) {
+ new=val;
+ } else {
+ break;
+ }
+ new+=vfo[active_receiver->id].filter;
+ if (new >= FILTERS) new=0;
+ if (new <0) new=FILTERS-1;
+ g_idle_add(ext_vfo_filter_changed, (gpointer) (uintptr_t) new);
+ break;
+ case MODE_UP: // key or wheel supported
+ case MODE_DOWN: // key or wheel supported
+ if (type == MIDI_KEY) {
+ new=(action == MODE_UP) ? 1 : -1;
+ } else if (type == MIDI_WHEEL) {
+ new=val;
+ } else {
+ break;
+ }
+ new+=vfo[active_receiver->id].mode;
+ if (new >= MODES) new=0;
+ if (new <0) new=MODES-1;
+ g_idle_add(ext_vfo_mode_changed, (gpointer) (uintptr_t) new);
+ break;
+ case PAN_LOW: // only wheel supported
+ if (type == MIDI_WHEEL) {
+ if (isTransmitting()) {
+ // TX panadapter affected
+ transmitter->panadapter_low += val;
+ } else {
+ active_receiver->panadapter_low += val;
+ }
+ }
+ break;
+ case RIT_ONOFF: // only key supported
+ if (type == MIDI_KEY) {
+ vfo[active_receiver->id].rit_enabled = !vfo[active_receiver->id].rit_enabled;
+ g_idle_add(ext_vfo_update, NULL);
+ }
+ break;
+ case RIT_VAL: // only wheel supported
+ if (type == MIDI_WHEEL) {
+ new = vfo[active_receiver->id].rit + val*rit_increment;
+ if (new > 9999) new= 9999;
+ if (new < -9999) new=-9999;
+ vfo[active_receiver->id].rit = new;
+ g_idle_add(ext_vfo_update, NULL);
+ }
+ break;
+ case PAN_HIGH: // only wheel supported
+ if (type == MIDI_WHEEL) {
+ if (mox) {
+ // TX panadapter affected
+ transmitter->panadapter_high += val;
+ } else {
+ active_receiver->panadapter_high += val;
+ }
+ }
+ break;
+ case PRE: // only key supported
+ if (filter_board == CHARLY25) {
+ new = active_receiver->preamp + active_receiver->dither;
+ new++;
+ if (new >2) new=0;
+ switch (new) {
+ case 0:
+ active_receiver->preamp=0;
+ active_receiver->dither=0;
+ break;
+ case 1:
+ active_receiver->preamp=1;
+ active_receiver->dither=0;
+ break;
+ case 2:
+ active_receiver->preamp=1;
+ active_receiver->dither=1;
+ break;
+ }
+ g_idle_add(ext_update_att_preamp, NULL);
+ } else {
+ new=active_receiver->preamp+1;
+ if (new > 1) new=0;
+ active_receiver->preamp= (new == 1);
+ }
+ break;
+ case ATT: // Key for ALEX attenuator, wheel or knob for slider
+ switch(type) {
+ case MIDI_KEY:
+ new=active_receiver->alex_attenuation + 1;
+ if (new > 3) new=0;
+ g_idle_add(ext_set_alex_attenuation, (gpointer)(uintptr_t)new);
+ g_idle_add(ext_update_att_preamp, NULL);
+ break;
+ case MIDI_WHEEL:
+ case MIDI_KNOB:
+ if (type == MIDI_WHEEL) {
+ new=adc_attenuation[active_receiver->adc] + val;
+ if (new > 31) new=31;
+ if (new < 0 ) new=0;
+ } else {
+ new=(31*val)/100;
+ }
+ dp=malloc(sizeof(double));
+ *dp=new;
+ g_idle_add(ext_set_attenuation_value,(gpointer) dp);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ fprintf(stderr,"Unimplemented in DoTheMidi: A=%d T=%d val=%d\n", action, type, val);
+ break;
+ }
+}
#include <ifaddrs.h>
#include <string.h>
#include <errno.h>
+#include <fcntl.h>
#include "discovered.h"
#include "discovery.h"
//
// We make a time-out of 3 secs, otherwise we might "hang" in connect()
//
+ flags=fcntl(discovery_socket, F_GETFL, 0);
+ fcntl(discovery_socket, F_SETFL, flags | O_NONBLOCK);
tv.tv_sec=3;
tv.tv_usec=0;
setsockopt(discovery_socket, SOL_SOCKET, SO_RCVTIMEO, (void *)&tv, sizeof(tv));
+
if (connect(discovery_socket, (const struct sockaddr *)&to_addr, sizeof(to_addr)) < 0) {
perror("discover: connect() failed for TCP discovery_socket:");
if (transmitter->auto_on) {
double ddb;
- int new_att;
+ static int new_att;
int newcal=info[5]!=old5_2;
old5_2=info[5];
switch(state) {
// Actually, we first adjust the attenuation (state=0),
// then do a PS reset (state=1), and then restart PS (state=2).
if (transmitter->attenuation != new_att) {
+ SetPSControl(transmitter->id, 1, 0, 0, 0);
transmitter->attenuation=new_att;
state=1;
}
static void auto_cb(GtkWidget *widget, gpointer data) {
transmitter->auto_on=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
- if(transmitter->auto_on) {
- transmitter->attenuation=31;
- } else {
+ if(!transmitter->auto_on) {
transmitter->attenuation=0;
}
+
}
static void resume_cb(GtkWidget *widget, gpointer data) {
#ifdef LOCALCW
#include "iambic.h"
#endif
+#ifdef MIDI
+#include "midi.h"
+#endif
#define min(x,y) (x<y?x:y)
#define max(x,y) (x<y?y:x)
gdk_window_set_cursor(gtk_widget_get_window(top_window),gdk_cursor_new(GDK_WATCH));
int rc;
+
+#ifdef MIDI
+ MIDIstartup();
+#endif
+
#ifdef __APPLE__
property_sem=sem_open("PROPERTY", O_CREAT, 0700, 0);
rc=(property_sem == SEM_FAILED);
} else {
int tval = atoi(&cmd_input[2]);
new_vol = (double) (tval * 60/100) - 10;
- //set_mic_gain(new_vol);
double *p_mic_gain=malloc(sizeof(double));
*p_mic_gain=new_vol;
- g_idle_add(update_mic_gain,(void *)p_mic_gain);
+ g_idle_add(ext_set_mic_gain,(void *)p_mic_gain);
}
} else {
if(len <=2) {
if((new_vol >= -10) && (new_vol <= 50)) {
double *p_mic_gain=malloc(sizeof(double));
*p_mic_gain=new_vol;
- g_idle_add(update_mic_gain,(void *)p_mic_gain);
+ g_idle_add(ext_set_mic_gain,(void *)p_mic_gain);
} else {
send_resp(client_sock,"?;");
}
void update_att_preamp(void) {
// CHARLY25: update the ATT/Pre buttons to the values of the active RX
+ // We should also set the attenuation for use in meter.c
if (filter_board == CHARLY25) {
char id[] = "x";
+ if (active_receiver->id != 0) {
+ active_receiver->alex_attenuation=0;
+ active_receiver->preamp=0;
+ active_receiver->dither=0;
+ adc_attenuation[active_receiver->adc] = 0;
+ }
sprintf(id, "%d", active_receiver->alex_attenuation);
+ adc_attenuation[active_receiver->adc] = 12*active_receiver->alex_attenuation;
gtk_combo_box_set_active_id(GTK_COMBO_BOX(c25_att_combobox), id);
sprintf(id, "%d", active_receiver->preamp + active_receiver->dither);
gtk_combo_box_set_active_id(GTK_COMBO_BOX(c25_preamp_combobox), id);
+ } else {
+ adc_attenuation[active_receiver->adc] = 10*active_receiver->alex_attenuation;
}
}
}
}
-int update_mic_gain(void *data) {
- set_mic_gain(*(double*)data);
- free(data);
- return 0;
-}
-
void set_linein_gain(int value) {
linein_gain=value;
if(display_sliders) {
transmitter_set_compressor(transmitter,gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
}
-void set_squelch(RECEIVER* rx) {
+void set_squelch() {
setSquelch(active_receiver);
if(display_sliders) {
gtk_range_set_value (GTK_RANGE(squelch_scale),active_receiver->squelch);
extern void sliders_update();
-extern void set_squelch(RECEIVER* rx);
+extern void set_squelch();
extern void set_compression(TRANSMITTER *tx);
#endif