Here is a list of actions currently implemented, in alphabetical
order. We also give for which types of MIDI events (KEY, KNOB,
-WHEEL, PITCH) this action is defined
+WHEEL) this action is defined. We further have a "dummy" action
+NONE which means that there is nothing to do.
Action Event Explanation
----------------------------------------------------------------------------------------------
AFGAIN KNOB,WHEEL AF audio volume
AGC KNOB,WHEEL AGC level
+AGCATTACK KEY cycle through AGC settings (fast/slow, etc.)
ATT KEY cycle through "ALEX attenuator" settings
ATT KNOB,WHEEL set RX attenuation (0-31dB)
BANDUP KEY,WHEEL cycle through the bands (upwards)
BANDDOWN KEY,WHEEL cycle through the bands (downwards)
-COMPRESS KNOB,WHEEL set TX compression level (if compression is activated)
+COMPRESS KNOB,WHEEL set TX compression level
+CTUN KEY toggle CTUN mode
FILTERUP KEY,WHEEL cycle through the filters (upwards)
FILTERDOWN KEY,WHEEL cycle through the filster (downwards)
+LOCK KEY lock VFO(s)
MICGAIN KNOB,WHEEL MIC gain
MODEUP KEY,WHEEL cycle through the modes (upwards)
MODEDOWN KEY,WHEEL cycle through the modes (downwards)
MOX KEY MOX on/off
+NB KEY cycle through NoiseBlanker settings
+NR KEY cycle through NoiseReduction settings
PANHIGH WHEEL change "high" level of current pan-adapter
PANLOW WHEEL change "bottom" level of current pan-adapter
PREAMP KEY cycle through preamp settings (only available for CHARLY25 filter boards)
-RITTOGGLE KEY RIT on/off
+PURESIGNAL KEY toggle PURESIGNAL (on/off)
+RITCLEAR KEY clear RIT frequency
RITVAL WHEEL change RIT frequency offset
RFPOWER KNOB,WHEEL TX power
+SPLIT KEY toggle Split mode
SWAPVFO KEY Swap VFO-A/VFO-B
TUNE KEY Tune on/off
VFO WHEEL change VFO frequency
+VFOA2B KEY frequency VFO A -> VFO B
+VFOB2A KEY frequency VFO B -> VFO A
+VOX KEY toggle VOX (on/off)
If you need more or different type of actions, the implementation should be straightforward.
*/
//
-// DL1YCF: If PortAudio is used instead of ALSO (e.g. on MacOS),
-// this file is not used (and replaced by portaudio.c).
+// If PortAudio is used instead of ALSA (e.g. on MacOS),
+// this file is not used (and replaced by portaudio.c).
#ifndef PORTAUDIO
return 0;
}
+int ext_vfo_a_to_b(void *data) {
+ vfo_a_to_b();
+ return 0;
+}
+
+int ext_vfo_b_to_a(void *data) {
+ vfo_b_to_a();
+ return 0;
+}
+
int ext_update_att_preamp(void *data) {
update_att_preamp();
return 0;
int ext_set_agc_gain(void *data);
int ext_set_drive(void *data);
int ext_vfo_a_swap_b(void *data);
+int ext_vfo_a_to_b(void *data);
+int ext_vfo_b_to_a(void *data);
int ext_update_att_preamp(void *data);
int ext_set_alex_attenuation(void *data);
int ext_set_attenuation_value(void *data);
break;
} else if ((info->maxFrequency +1) == ((info+1)->minFrequency) &&
flow>=info->minFrequency && fhigh <= (info+1)->maxFrequency) {
- // DL1YCF:
+ //
// Sometimes in DigiMode operation, the nominal USB TX channel crosses
// a sub-band boundary. Example: FT8 on 17m, VFO set to 18099 kHz because
// FT8 signal is generated at 18100.8 kHz with an AF signal of 1800 Hz.
// either sub-band. We can return the info for the lower sub-band if
// the band and transmit data is the same in both cases. If they are
// different, we have found no match so far.
+ //
if ((info->band == (info+1)->band) && (info->transmit == (info+1)->transmit)) {
result = info->info;
break;
*
* RF1: ADC noise (16-bit ADC) plus a 800 Hz signal at -100dBm
* RF2: ADC noise (16-bit ADC) plus a 2000 Hz signal at - 80dBm
- * RF3: TX feedback signal with some distortion. Signal strength
- * according to the "TX drive" and "TX ATT" settings.
- * RF4: normalized undistorted TX signal with a peak value of 0.400
+ * RF3: TX feedback signal with some distortion.
+ * RF4: normalized undistorted TX signal with a peak value of 0.407
*
- * RF1 and RF2 respect the Preamp and Attenuator settings
+ * RF1 and RF2 signal strenght vary according to Preamp and Attenuator settings
+ * RF3 signal strength varies according to TX-drive and TX-ATT settings
+ * RF4 signal strength is normalized to amplitude of 0.407
*
- * 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,
+ * RF1 and RF2 are associated with ADC1 and ADC2.
+ * RF3 also goes to ADC1 upon TX
+ * RF4 is the TX DAC signal. Upon TX, it goes to RX2 for Metis, RX4 for Hermes, and RX5 beyond.
*
- * The connection upon transmitting depends on the DEVICE, namely
+ * The SDR application has to make the proper ADC settings!
*
- * 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)
+ * Without PURESIGNAL, the standard association is:
+ * RX1=ADC1, RX2=ADC2 (ADC2 not present for METIS and ANAN-10/10E/100)
*
+ * When using PURESIGNAL, the standard association is:
+ *
+ * DEVICE=METIS: RX1=ADC1, RX2=TX-DAC
+ * DEVICE=HERMES: RX1=ADC1, RX2=ADC1, RX3=ADC2, RX4=TX-DAC
+ * DEVICE=ORION: RX1=ADC1, RX2=ADC1, RX3=ADC3, RX4=ADC1, RX5=TX-DAC (also for ANGELIA and ORION2)
+ *
+ * Note that for STEMlab (RedPitaya) based SDRs, there is usually a fixed association
+ *
+ * RX1=ADC1, RX2=ADC2, RX3=ADC2, RX4=TX-DAC
+ *
+ * And this setting is NOT AFFECTED by the ADC settings in the HPSDR protocol.
*
* Audio sent to the "radio" is played via the first available output channel.
* This works on MacOS (PORTAUDIO) and Linux (ALSA).
static int alexTRdisable=-1;
static int vna=-1;
static int c25_ext_board_i2c_data=-1;
-static int rx_adc[7]={0,1,1,2,-1,-1,-1};
+static int rx_adc[7]={-1,-1,-1,-1,-1,-1,-1};
static int cw_hang = -1;
static int cw_reversed = -1;
static int cw_speed = -1;
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;
+ ismetis=ishermes=isorion=isc25=0;
switch (DEVICE) {
case 0: fprintf(stderr,"DEVICE is METIS\n"); ismetis=1; break;
case 1: fprintf(stderr,"DEVICE is HERMES\n"); ishermes=1; break;
chk_data((frame[2] & 0x30) >> 4, rx_adc[6], "RX7 ADC");
chk_data((frame[3] & 0x1f), txatt, "TX ATT");
txatt_dbl=pow(10.0, -0.05*(double) txatt);
+ if (isc25) {
+ // RedPitaya: Hard-wired ADC settings.
+ rx_adc[0]=0;
+ rx_adc[1]=1;
+ rx_adc[2]=1;
+ }
break;
case 30:
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];
- //
+ // ADC1: noise + weak tone on RX, feedback sig. on TX
+ if (ptt) {
+ i1=isample[rxptr]*txdrv_dbl;
+ q1=qsample[rxptr]*txdrv_dbl;
+ fac=IM3a+IM3b*(i1*i1+q1*q1);
+ rf1isample= (txatt_dbl*i1*fac+noiseItab[noiseIQpt]) * 8388607.0;
+ rf1qsample= (txatt_dbl*q1*fac+noiseItab[noiseIQpt]) * 8388607.0;
+ } else {
+ 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, with some ADC noise
- //
- i1=isample[rxptr]*txdrv_dbl;
- q1=qsample[rxptr]*txdrv_dbl;
- fac=IM3a+IM3b*(i1*i1+q1*q1);
- rf3isample= (txatt_dbl*i1*fac+noiseItab[noiseIQpt]) * 8388607.0;
- rf3qsample= (txatt_dbl*q1*fac+noiseItab[noiseIQpt]) * 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
- if (ptt && ismetis) {
- myisample=rf3isample;
- myqsample=rf3qsample;
- } else {
- myisample=rf1isample;
- myqsample=rf1qsample;
- }
- break;
- case 1: // RX2
- if (ptt && ismetis) {
- myisample=rf4isample;
- myqsample=rf4qsample;
- } else {
- myisample=rf2isample;
- myqsample=rf2qsample;
- }
+ switch (rx_adc[k]) {
+ case 0: // ADC1
+ myisample=rf1isample;
+ myqsample=rf1qsample;
break;
- case 2:
- if (ptt && ishermes) {
- myisample=rf3isample;
- myqsample=rf3qsample;
- } else {
- myisample=rf2isample;
- myqsample=rf2qsample;
- }
+ case 1: // ADC2
+ myisample=rf2isample;
+ myqsample=rf2qsample;
break;
- case 3: // RX4
- if (ptt && ishermes) {
- myisample=rf4isample;
- myqsample=rf4qsample;
- } else if (ptt && isorion) {
- myisample=rf3isample;
- myqsample=rf3qsample;
- }
- break;
- case 4: // RX5
- if (ptt && isorion) {
- myisample=rf4isample;
- myqsample=rf4qsample;
- }
+ default:
+ myisample=0;
+ myqsample=0;
break;
}
+ if (ismetis && ptt && (k==1)) {
+ // METIS: TX DAC signal goes to RX2 when TXing
+ myisample=rf4isample;
+ myqsample=rf4qsample;
+ }
+ if (ishermes && ptt && (k==3)) {
+ // HERMES: TX DAC signal goes to RX4 when TXing
+ myisample=rf4isample;
+ myqsample=rf4qsample;
+ }
+ if (isorion && ptt && (k==4)) {
+ // ANGELIA and beyond: TX DAC signal goes to RX5 when TXing
+ myisample=rf4isample;
+ myqsample=rf4qsample;
+ }
*pointer++ = (myisample >> 16) & 0xFF;
*pointer++ = (myisample >> 8) & 0xFF;
*pointer++ = (myisample >> 0) & 0xFF;
char wisdom_directory[1024];
int rc;
- fprintf(stderr,"init\n");
+ //fprintf(stderr,"init\n");
audio_get_cards();
fprintf(stderr,"display_width=%d display_height=%d\n", display_width, display_height);
- fprintf(stderr,"create top level window\n");
+ //fprintf(stderr,"create top level window\n");
top_window = gtk_application_window_new (app);
if(full_screen) {
-fprintf(stderr,"full screen\n");
+ fprintf(stderr,"full screen\n");
gtk_window_fullscreen(GTK_WINDOW(top_window));
}
gtk_widget_set_size_request(top_window, display_width, display_height);
gtk_window_set_title (GTK_WINDOW (top_window), "piHPSDR");
gtk_window_set_position(GTK_WINDOW(top_window),GTK_WIN_POS_CENTER_ALWAYS);
gtk_window_set_resizable(GTK_WINDOW(top_window), FALSE);
- fprintf(stderr,"setting top window icon\n");
+ //fprintf(stderr,"setting top window icon\n");
GError *error;
if(!gtk_window_set_icon_from_file (GTK_WINDOW(top_window), "hpsdr.png", &error)) {
fprintf(stderr,"Warning: failed to set icon for top_window\n");
//fixed=gtk_fixed_new();
//gtk_container_add(GTK_CONTAINER(top_window), fixed);
-fprintf(stderr,"create grid\n");
+//fprintf(stderr,"create grid\n");
grid = gtk_grid_new();
gtk_widget_set_size_request(grid, display_width, display_height);
gtk_grid_set_row_homogeneous(GTK_GRID(grid),FALSE);
gtk_grid_set_column_homogeneous(GTK_GRID(grid),FALSE);
-fprintf(stderr,"add grid\n");
+//fprintf(stderr,"add grid\n");
gtk_container_add (GTK_CONTAINER (top_window), grid);
-fprintf(stderr,"create image\n");
+//fprintf(stderr,"create image\n");
GtkWidget *image=gtk_image_new_from_file("hpsdr.png");
-fprintf(stderr,"add image to grid\n");
+//fprintf(stderr,"add image to grid\n");
gtk_grid_attach(GTK_GRID(grid), image, 0, 0, 1, 4);
-fprintf(stderr,"create pi label\n");
+//fprintf(stderr,"create pi label\n");
char build[64];
sprintf(build,"build: %s %s",build_date, version);
GtkWidget *pi_label=gtk_label_new("piHPSDR by John Melton g0orx/n6lyt");
gtk_label_set_justify(GTK_LABEL(pi_label),GTK_JUSTIFY_LEFT);
gtk_widget_show(pi_label);
-fprintf(stderr,"add pi label to grid\n");
+//fprintf(stderr,"add pi label to grid\n");
gtk_grid_attach(GTK_GRID(grid),pi_label,1,0,1,1);
-fprintf(stderr,"create build label\n");
+//fprintf(stderr,"create build label\n");
GtkWidget *build_date_label=gtk_label_new(build);
gtk_label_set_justify(GTK_LABEL(build_date_label),GTK_JUSTIFY_LEFT);
gtk_widget_show(build_date_label);
-fprintf(stderr,"add build label to grid\n");
+//fprintf(stderr,"add build label to grid\n");
gtk_grid_attach(GTK_GRID(grid),build_date_label,1,1,1,1);
-fprintf(stderr,"create status\n");
+//fprintf(stderr,"create status\n");
status=gtk_label_new("");
gtk_label_set_justify(GTK_LABEL(status),GTK_JUSTIFY_LEFT);
//gtk_widget_override_font(status, pango_font_description_from_string("FreeMono 18"));
gtk_widget_show(status);
-fprintf(stderr,"add status to grid\n");
+//fprintf(stderr,"add status to grid\n");
gtk_grid_attach(GTK_GRID(grid), status, 1, 3, 1, 1);
/*
sprintf(name,"org.g0orx.pihpsdr.pid%d",getpid());
-fprintf(stderr,"gtk_application_new: %s\n",name);
+//fprintf(stderr,"gtk_application_new: %s\n",name);
pihpsdr=gtk_application_new(name, G_APPLICATION_FLAGS_NONE);
g_signal_connect(pihpsdr, "activate", G_CALLBACK(activate_pihpsdr), NULL);
status=g_application_run(G_APPLICATION(pihpsdr), argc, argv);
-fprintf(stderr,"exiting ...\n");
+ fprintf(stderr,"exiting ...\n");
g_object_unref(pihpsdr);
return status;
}
//
// MIDIaction encodes the "action" to be taken in Layer3
+// (sorted alphabetically)
//
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
+ ACTION_NONE=0, // No-Op (unassigned key)
+ AGC, // AGC level
+ AGCATTACK, // AGC ATTACK (cycle fast/med/slow etc.)
+ ATT, // Step attenuator or Programmable attenuator
+ AF_GAIN, // AF gain
+ BAND_DOWN, // cycle through bands downwards
+ BAND_UP, // cycle through bands upwards
+ COMPRESS, // TX compressor value
+ CTUN, // CTUN on/off
+ FILTER_UP, // cycle through filters upwards
+ FILTER_DOWN, // cycle through filters downwards
+ MIC_VOLUME, // MIC gain
+ LOCK, // disable frequency changes
+ MODE_UP, // cycle through modes upwards
+ MODE_DOWN, // cycle through modes downwards
+ MOX, // toggle "mox" state
+ NB, // cycle through NoiseBlanker states (none, NB, NB2)
+ NR, // cycle through NoiseReduction states (none, NR, NR2)
+ PRE, // preamp on/off
+ PAN_HIGH, // "high" value of current panadapter
+ PAN_LOW, // "low" value of current panadapter
+ PS, // PURESIGNAL on/off
+ RIT_CLEAR, // clear RIT value
+ RIT_VAL, // change RIT value
+ SPLIT, // Split on/off
+ SWAP_VFO, // swap VFO A/B frequency
+ TUNE, // toggle "tune" state
+ TX_DRIVE, // RF output power
+ VFO, // change VFO frequency
+ VFO_A2B, // VFO A -> B
+ VFO_B2A, // VFO B -> A
+ VOX // VOX on/off
};
//
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)
+ int delay; // Wheel only: delay (msec) before next message is given upstream
enum MIDIaction action; // SDR "action" to generate
- struct desc *next; // Next defined action for a controller/key with that note value.
+ struct desc *next; // Next defined action for a controller/key with that note value (NULL for end of list)
};
struct {
//
// 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.
+// The implementation of DoTheMIDI is tightly bound to pihpsr and contains
+// tons of invocations of g_idle_add with routines from ext.c
//
void DoTheMidi(enum MIDIaction code, enum MIDItype type, int val);
#
# 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)
+# fits best for radios with a programmable attenuator (0-31 dB).
+# The button (Key 2) also works for STEMlab and CHARLY25
+#
+# The preamp button (Key 1) only has function with STEMlab and CHARLY25
+#
+# Do not assign the "Key" with number 31. You will unintentionally
+# "push" it most of the times you change the VFO frequency.
+#
+# NOTE: we could just leave out all lines with ACTION=NONE. In this case
+# a diagnostic message ("unknown MIDI event") is printed to stderr.
#
DEVICE=CMD PL-1
+#
+# Big Wheel and Big Slider
+#
CTRL=31 WHEEL THR=59 61 63 65 67 69 ACTION=VFO # Big wheel: : main VFO knob
+KEY=31 ACTION=NONE # Button integrated in the big wheel (do not assign)
PITCH ACTION=AFGAIN # Big slider : AF gain
+#
+# 8 Knobs (top left).
+# Note that you can push each knob and this generates
+# Note On/Off messages for KEY=0...8 which we do not
+# assign: we want to glue labels on the Controller and therefore
+# we do not have two labels per knob.
+#
+CTRL=0 WHEEL THR=-1 -1 63 65 128 128 ACTION=ATT # Knob 1 : RX att
+KEY=0 ACTION=NONE # Push Knob 1 : (unassigned)
+CTRL=1 WHEEL THR=-1 -1 63 65 128 128 ACTION=COMPRESS # Knob 2 : TX compression
+KEY=1 ACTION=NONE # Push Knob 1 : (unassigned)
+CTRL=2 WHEEL THR=-1 -1 63 65 128 128 ACTION=RITVAL # Knob 3 : RIT value
+KEY=2 ACTION=NONE # Push Knob 1 : (unassigned)
+CTRL=3 WHEEL THR=-1 -1 63 65 128 128 ACTION=PANLOW # Knob 4 : Panadapter low
+KEY=4 ACTION=NONE # Push Knob 1 : (unassigned)
+CTRL=4 WHEEL THR=-1 -1 63 65 128 128 ACTION=AGC # Knob 5 : AGC
+KEY=4 ACTION=NONE # Push Knob 1 : (unassigned)
+CTRL=5 WHEEL THR=-1 -1 63 65 128 128 ACTION=MICGAIN # Knob 6 : MIC gain
+KEY=5 ACTION=NONE # Push Knob 1 : (unassigned)
+CTRL=6 WHEEL THR=-1 -1 63 65 128 128 ACTION=RFPOWER # Knob 7 : TX drive
+KEY=6 ACTION=NONE # Push Knob 1 : (unassigned)
+CTRL=7 WHEEL THR=-1 -1 63 65 128 128 ACTION=FILTERUP # Knob 8 : cycle through the filters
+KEY=7 ACTION=NONE # Push Knob 1 : (unassigned)
+#
+# 8 Keys (below the 8 Knobs)
+#
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=18 ACTION=RITCLEAR # Key 3 : Clear RIT value and disable RIT
+KEY=19 ACTION=CTUN # Key 4 : toggle CTUN
+KEY=20 ACTION=NOISEBLANKER # Key 5 : cycle through NB settings
+KEY=21 ACTION=NOISEREDUCTION # Key 6 : cycle through NR settings
+KEY=22 ACTION=VOX # Key 7 : toggle VOX
+KEY=23 ACTION=AGCATTACK # Key 8 : cycle AGC fast/medium/slow
+#
+# "other" keys
+# Note that the "DECK" key switches the MIDI channel of the device.
+#
KEY=24 ACTION=TUNE # LOAD button: TUNE on/off
+KEY=25 ACTION=LOCK # LOCK button: Lock VFO(s)
+KEY=26 ACTION=PURESIGNAL # DECK button: toggle PURESIGNAL
KEY=27 ACTION=SWAPVFO # SCRATCH button: Swap VFOs A and B
+KEY=31 ACTION=NONE # Button integrated in the big wheel (do not assign)
+KEY=32 ACTION=VFOA2B # SYNC button: Frequency VFO A -> VFO B
+KEY=33 ACTION=VFOB2A # TAP button: Frequency VFO B -> VFO A
KEY=34 ACTION=MOX # CUE button: MOX on/off
+KEY=35 ACTION=SPLIT # >|| button: toggle Split
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
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;
desc=desc->next;
}
}
+ if (!desc) {
+ // Nothing found. This is nothing to worry about, but log the key to stderr
+ if (event == MIDI_PITCH) fprintf(stderr, "Unassigned PitchBend Value=%d\n", val);
+ if (event == MIDI_NOTE ) fprintf(stderr, "Unassigned Key Note=%d Val=%d\n", note, val);
+ if (event == MIDI_CTRL ) fprintf(stderr, "Unassigned Controller Ctl=%d Val=%d\n", note, val);
+ }
}
/*
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"},
+ { AGCATTACK, "AGCATTACK"},
+ { ATT, "ATT"},
{ BAND_DOWN, "BANDDOWN"},
- { FILTER_UP, "FILTERUP"},
+ { BAND_UP, "BANDUP"},
+ { COMPRESS, "COMPRESS"},
+ { CTUN, "CTUN"},
{ FILTER_DOWN, "FILTERDOWN"},
- { MODE_UP, "MODEUP"},
+ { FILTER_UP, "FILTERUP"},
+ { LOCK, "LOCK"},
+ { MIC_VOLUME, "MICGAIN"},
{ MODE_DOWN, "MODEDOWN"},
+ { MODE_UP, "MODEUP"},
+ { MOX, "MOX"},
+ { NB, "NOISEBLANKER"},
+ { NR, "NOISEREDUCTION"},
+ { PAN_HIGH, "PANHIGH"},
+ { PAN_LOW, "PANLOW"},
+ { PRE, "PREAMP"},
+ { PS, "PURESIGNAL"},
+ { RIT_CLEAR, "RITCLEAR"},
+ { RIT_VAL, "RITVAL"},
+ { SPLIT, "SPLIT"},
{ SWAP_VFO, "SWAPVFO"},
+ { TUNE, "TUNE"},
+ { TX_DRIVE, "RFPOWER"},
+ { VFO, "VFO"},
+ { VFO_A2B, "VFOA2B"},
+ { VFO_B2A, "VFOB2A"},
+ { VOX, "VOX"},
+ { ACTION_NONE, "NONE"},
{ ACTION_NONE, NULL}
};
int i=0;
for (i=0; 1; i++) {
- if (ActionTable[i].str == NULL) return ACTION_NONE;
+ if (ActionTable[i].str == NULL) {
+ fprintf(stderr,"MIDI: action keyword %s NOT FOUND.\n", s);
+ return ACTION_NONE;
+ }
if (!strcmp(s, ActionTable[i].str)) return ActionTable[i].action;
}
/* NOTREACHED */
continue; // nothing more in this line
}
chan=-1; // default: any channel
- lt3=lt2=lt1=0;
- ut3=ut2=ut1=127;
+ lt3=lt2=lt1=-1;
+ ut3=ut2=ut1=128;
onoff=0;
event=EVENT_NONE;
type=TYPE_NONE;
#include "new_menu.h"
#include "sliders.h"
#include "ext.h"
+#include "agc.h"
#include "midi.h"
void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) {
}
break;
case VFO: // only wheel supported
- if (type == MIDI_WHEEL) g_idle_add(ext_vfo_step, (gpointer)(uintptr_t) val);
+ if (type == MIDI_WHEEL) {
+ g_idle_add(ext_vfo_step, (gpointer)(uintptr_t) val);
+ }
break;
case TUNE: // only key supported
if (type == MIDI_KEY) {
}
}
break;
- case RIT_ONOFF: // only key supported
+ case RIT_CLEAR: // only key supported
if (type == MIDI_KEY) {
- vfo[active_receiver->id].rit_enabled = !vfo[active_receiver->id].rit_enabled;
+ // this clears the RIT value and disables RIT
+ vfo[active_receiver->id].rit = new;
+ vfo[active_receiver->id].rit_enabled = 0;
g_idle_add(ext_vfo_update, NULL);
}
break;
case RIT_VAL: // only wheel supported
if (type == MIDI_WHEEL) {
+ // This changes the RIT value. If a value of 0 is reached,
+ // RIT is disabled
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;
+ vfo[active_receiver->id].rit_enabled = (new != 0);
g_idle_add(ext_vfo_update, NULL);
}
break;
}
g_idle_add(ext_update_att_preamp, NULL);
} else {
+ // Note that these preamps are not present in most
+ // SDR hardware.
new=active_receiver->preamp+1;
if (new > 1) new=0;
active_receiver->preamp= (new == 1);
}
break;
case COMPRESS:
+ // Use values in the range 0 ... 20
+ if (type == MIDI_WHEEL) {
+ dnew=transmitter->compressor_level + val;
+ if (dnew > 20.0) dnew=20.0;
+ if (dnew < 0 ) dnew=0;
+ } else if (type == MIDI_KNOB){
+ dnew=(20.0*val)/100.0;
+ } else {
+ break;
+ }
+ transmitter->compressor_level=dnew;
+ if (dnew < 0.5) transmitter->compressor=0;
+ if (dnew > 0.5) transmitter->compressor=1;
+ g_idle_add(ext_vfo_update, NULL);
+ break;
+ case NB:
+ // cycle through NoiseBlanker settings
+ if (active_receiver->nb) {
+ active_receiver->nb = 0;
+ active_receiver->nb2= 1;
+ } else if (active_receiver->nb2) {
+ active_receiver->nb = 0;
+ active_receiver->nb2= 0;
+ } else {
+ active_receiver->nb = 1;
+ active_receiver->nb2= 0;
+ }
+ g_idle_add(ext_vfo_update, NULL);
+ break;
+ case NR:
+ // cycle through NoiseReduction settings
+ if (active_receiver->nr) {
+ active_receiver->nr = 0;
+ active_receiver->nr2= 1;
+ } else if (active_receiver->nr2) {
+ active_receiver->nr = 0;
+ active_receiver->nr2= 0;
+ } else {
+ active_receiver->nr = 1;
+ active_receiver->nr2= 0;
+ }
+ g_idle_add(ext_vfo_update, NULL);
+ break;
+ case VOX:
+ // toggle VOX
+ vox_enabled = !vox_enabled;
+ g_idle_add(ext_vfo_update, NULL);
+ break;
+ case CTUN:
+ // toggle CTUN
+ new=active_receiver->id;
+ if(!vfo[new].ctun) {
+ vfo[new].ctun=1;
+ vfo[new].offset=0;
+ } else {
+ vfo[new].ctun=0;
+ }
+ vfo[new].ctun_frequency=vfo[new].frequency;
+ set_offset(active_receiver,vfo[new].offset);
+ g_idle_add(ext_vfo_update, NULL);
+ break;
+ case PS:
+ // toggle PURESIGNAL
+ new=!(transmitter->puresignal);
+ g_idle_add(ext_tx_set_ps,(gpointer) (uintptr_t) new);
+ break;
+ case SPLIT:
+ // toggle split mode
+ if(!split) {
+ split=1;
+ tx_set_mode(transmitter,vfo[VFO_B].mode);
+ } else {
+ split=0;
+ tx_set_mode(transmitter,vfo[VFO_A].mode);
+ }
+ g_idle_add(ext_vfo_update, NULL);
+ break;
+ case VFO_A2B:
+ g_idle_add(ext_vfo_a_to_b, NULL);
+ break;
+ case VFO_B2A:
+ g_idle_add(ext_vfo_b_to_a, NULL);
+ break;
+ case LOCK:
+ locked=!locked;
+ g_idle_add(ext_vfo_update, NULL);
+ break;
+ case AGCATTACK:
+ new=active_receiver->agc + 1;
+ if (new > AGC_FAST) new=0;
+ active_receiver->agc=new;
+ g_idle_add(ext_vfo_update, NULL);
+ break;
case ACTION_NONE:
- fprintf(stderr,"Unimplemented in DoTheMidi: A=%d T=%d val=%d\n", action, type, val);
+ // No error message, this is the "official" action for un-used controller buttons.
break;
+ default:
+ // This means we have forgotten to implement an action
+ fprintf(stderr,"Unimplemented MIDI action: A=%d\n", (int) action);
}
}
int nreceivers;
#ifdef PURESIGNAL
- // DL1YCF:
// for PureSignal, the number of receivers needed is hard-coded below.
// we need at least 3 (for RX), and up to 5 for Orion2 boards, since
// the TX DAC channel is hard-wired to RX5.
int nreceivers;
#ifdef PURESIGNAL
- // DL1YCF:
// for PureSignal, the number of receivers needed is hard-coded below.
// we need at least 3 (for RX), and up to 5 for Orion2 boards, since
// the TX DAC channel is hard-wired to RX5.
// Upon TX, we might have to activate a different RX path for the
// attenuated feedback signal. Use feedback_antenna == 0, if
// the feedback signal is routed automatically/internally
+ // If feedback is to the second ADC, leave RX1 ANT settings untouched
//
- if (isTransmitting() && transmitter->puresignal) i=receiver[PS_RX_FEEDBACK]->feedback_antenna;
+ if (isTransmitting() && transmitter->puresignal && receiver[PS_RX_FEEDBACK]->adc == 0) i=receiver[PS_RX_FEEDBACK]->feedback_antenna;
#endif
switch(i) {
- case 3: // Alex: RX2 IN, ANAN: EXT1, ANAN7000: EXT
- output_buffer[C3]|=0xC0;
+ case 3: // Alex: RX2 IN, ANAN: EXT1, ANAN7000: still uses internal feedback
+ output_buffer[C3]|=0x80;
break;
case 4: // Alex: RX1 IN, ANAN: EXT2, ANAN7000: RX BYPASS
output_buffer[C3]|=0xA0;
break;
default:
+ // RX1_OUT and RX1_ANT bits remain zero
break;
}
-// TODO - add Alex TX relay, duplex, receivers Mercury board frequency
output_buffer[C4]=0x04; // duplex
// 0 ... 7 maps on 1 ... 8 receivers
output_buffer[C0]=0x04+(current_rx*2);
#ifdef PURESIGNAL
int v=receiver[current_rx/2]->id;
- // DL1YCF: for the "last" receiver, v is out of range. In this case,
- // use TX frequency also while receiving
+ // for the "last" receiver, v is out of range. In this case,
+ // use TX frequency also while receiving
if((isTransmitting() && transmitter->puresignal) || (v >= MAX_VFOS)) {
long long txFrequency;
if(active_receiver->id==VFO_A) {
output_buffer[C4]=0x00;
if(radio->device==DEVICE_HERMES || radio->device==DEVICE_ANGELIA || radio->device==DEVICE_ORION || radio->device==DEVICE_ORION2) {
- // DL1YCF: if attenuation is zero, then disable attenuator
+ // if attenuation is zero, then disable attenuator
i = adc_attenuation[receiver[0]->adc] & 0x1F;
if (i >0) output_buffer[C4]=0x20| i;
} else {
output_buffer[C1]=0x00;
if(receivers==2) {
if(radio->device==DEVICE_HERMES || radio->device==DEVICE_ANGELIA || radio->device==DEVICE_ORION || radio->device==DEVICE_ORION2) {
- // DL1YCF: if attenuation is zero, then disable attenuator
+ // if attenuation is zero, then disable attenuator
i = adc_attenuation[receiver[1]->adc] & 0x1F;
if (i > 0) output_buffer[C1]=0x20|i;
}
// need to add tx attenuation and rx ADC selection
output_buffer[C0]=0x1C;
output_buffer[C1]=0x00;
+ output_buffer[C2]=0x00;
#ifdef PURESIGNAL
-
- // The whole setup here implicitly assumes
- // that receiver[0]->adc is 0 and receiver[1]->adc is 1
-
- // This is the correct setting for DEVICE_METIS and DEVICE_HERMES
- output_buffer[C1]|=receiver[0]->adc; // RX1 bound to ADC0
- output_buffer[C1]|=(receiver[0]->adc<<2); // RX2 bound to ADC0
- output_buffer[C1]|=receiver[1]->adc<<4; // RX3 bound to ADC1
-
- // DL1YCF:
- // For Orion, Angelia and OrionMk2 RX4 must show the FeedBack signal.
- // In most cases this is routed back to RX1 so we need ADC0 for RX4
- // Since I could test this only on my ANAN7000, I changed to code
- // to associate RX4 with ADC0 only on Orion2 -- but I guess this will
- // be necessary for other SDRs as well.
-
- if (device == DEVICE_ORION2) {
- output_buffer[C1]|=(receiver[0]->adc<<6); // This works on ANAN7000
- } else {
- output_buffer[C1]|=(receiver[1]->adc<<6); // Does this work for somebody?
+ // if n_adc == 1, there is only a single ADC, so we can leave everything
+ // set to zero
+ if (n_adc > 1) {
+ // Angelia, Orion, Orion2 have two ADCs, so we use the ADC settings from the menu
+ output_buffer[C1]|=receiver[0]->adc; // RX1 bound to ADC of first receiver
+ output_buffer[C1]|=(receiver[1]->adc<<2); // RX2 actually unsused with PURESIGNAL
+ output_buffer[C1]|=receiver[1]->adc<<4; // RX3 bound to ADC of second receiver
+ output_buffer[C1]|=(receiver[PS_RX_FEEDBACK]->adc<<6); // RX4 is PS_RX_Feedbacka
+ // Usually ADC0, but if feedback is to
+ // RX2 input it must be ADC1 (see ps_menu.c)
+ // RX5 is hard-wired to the TX DAC and needs no ADC setting.
}
- output_buffer[C2]=0x00;
- if(transmitter->puresignal) {
- // This should not be necessary since RX5 is hard-wired to the TX DAC on TX
- output_buffer[C2]|=receiver[2]->adc;
- }
#else
output_buffer[C1]|=receiver[0]->adc;
output_buffer[C1]|=(receiver[1]->adc<<2);
case 10:
output_buffer[C0]=0x24;
output_buffer[C1]=0x00;
+
if(isTransmitting()) {
- output_buffer[C1]|=0x80; // ground RX2 on transmit, bit0-6 are Alex2 filters
+#ifdef PURESIGNAL
+ // If we are using the RX2 jack for PURESIGNAL RX feedback, then we MUST NOT ground
+ // the ADC2 input upon TX.
+ if (transmitter->puresignal && receiver[PS_RX_FEEDBACK]->adc == 1) {
+ // Note that this statement seems to have no effect since
+ // one cannot goto to "manual filter setting" for the ALEX2 board individually
+ output_buffer[C1]|=0x40; // Set ADC2 filter board to "ByPass"
+ } else
+#endif
+
+ output_buffer[C1]|=0x80; // ground RX2 on transmit, bit0-6 are Alex2 filters
}
output_buffer[C2]=0x00;
if(receiver[0]->alex_antenna==5) { // XVTR
current_rx=0;
command=1;
#else
- // DL1YCF this is the original code, which does not do what it pretends ....
+ // DL1YCF: this is the original code, which does not do what it pretends ....
// send commands twice
command=1;
do {
#ifdef PORTAUDIO
//
-// DL1YCF: if PortAudio is NOT used, this file is empty, and audio.c
-// is used instead.
-// Here we also implement two (hopefully useful) functions:
-// - a dummy two-tone 'Microphone' device
+// Contribution from DL1YCF (Christoph van Wullen)
+//
+// Alternate "audio" module using PORTAUDIO instead of ALSA
+// (e.g. on MacOS)
+//
+// If PortAudio is NOT used, this file is empty, and audio.c
+// is used instead.
//
#include <gtk/gtk.h>
}
//
-//
-// DL1YCF: had repeated seg-faults, possibly this is not thread-safe.
-// executing it at regular intervals in the glib main loop
-// does the same thing.
-// We now call this function every 100 msec, therefore we can
-// adjust the tx attenuation upon each invocation.
-// The massive alloc/free of the label is completely unnecessary
-// and has been removed, the small string to be displayed is
-// prepeared in "label".
+// This is called every 100 msec and therefore
+// must be a state machine
//
static int info_thread(gpointer arg) {
static int info[INFO_SIZE];
return TRUE;
}
+//
+// Set "RX1 ANT", "RX1 OUT", and ADC settings for the PS feedback signal
+//
static void ps_ant_cb(GtkWidget *widget, gpointer data) {
+ int val = (int) (uintptr_t) data;
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
- receiver[PS_RX_FEEDBACK]->feedback_antenna = (int) (uintptr_t) data;
+ switch (val) {
+ case 0: // AUTO (Internal), feedback goes to first ADC
+ case 3: // EXT1, feedback goes to first ADC
+ case 4: // EXT2, feedback goes to first ADC
+ receiver[PS_RX_FEEDBACK]->feedback_antenna = (int) (uintptr_t) data;
+ receiver[PS_RX_FEEDBACK]->adc = 0;
+ break;
+ case 99: // RX2, feedback goes to second ADC
+ receiver[PS_RX_FEEDBACK]->feedback_antenna = 0;
+ receiver[PS_RX_FEEDBACK]->adc = 1;
+ break;
+ }
}
}
static void auto_cb(GtkWidget *widget, gpointer data) {
transmitter->auto_on=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ // switching OFF auto sets the attenuation to zero
if(!transmitter->auto_on) {
transmitter->attenuation=0;
}
}
static void resume_cb(GtkWidget *widget, gpointer data) {
+ // Set the attenuation to zero if auto-adjusting and resuming.
+ // A very high attenuation value here could lead to no PS calculation
+ // done in WDSP, and hence no attenuation adjustment.
+ // If not auto-adjusting, do not change attenuation value.
+ if(transmitter->auto_on) {
+ transmitter->attenuation=0;
+ }
SetPSControl(transmitter->id, 0, 0, 1, 0);
}
}
static void reset_cb(GtkWidget *widget, gpointer data) {
- transmitter->attenuation=0;
SetPSControl(transmitter->id, 1, 0, 0, 0);
}
row++;
col=0;
+ //
+ // Selection of feedback path for PURESIGNAL
+ //
+ // AUTO Using internal feedback (to ADC0)
+ // EXT1 Using EXT1 jacket (to ADC0), ANAN-7000: still uses AUTO
+ // EXT2 Using EXT2 jacket (to ADC0), ANAN-7000: "EXT2 is called RX Bypass"
+ // RX2 Using RX2 jacket (to ADC1)
+ //
GtkWidget *ps_ant_label=gtk_label_new("PS FeedBk ANT:");
gtk_widget_show(ps_ant_label);
gtk_grid_attach(GTK_GRID(grid), ps_ant_label, col, row, 1, 1);
GtkWidget *ps_ant_auto=gtk_radio_button_new_with_label(NULL,"AUTO");
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_auto),
- (receiver[PS_RX_FEEDBACK]->feedback_antenna!=3) && (receiver[PS_RX_FEEDBACK]->feedback_antenna!=4));
+ (receiver[PS_RX_FEEDBACK]->feedback_antenna == 0) && (receiver[PS_RX_FEEDBACK]->adc == 0));
gtk_widget_show(ps_ant_auto);
gtk_grid_attach(GTK_GRID(grid), ps_ant_auto, col, row, 1, 1);
g_signal_connect(ps_ant_auto,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 0);
col++;
GtkWidget *ps_ant_ext1=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ps_ant_auto),"EXT1");
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext1), receiver[PS_RX_FEEDBACK]->feedback_antenna==3);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext1),
+ (receiver[PS_RX_FEEDBACK]->feedback_antenna==3) && (receiver[PS_RX_FEEDBACK]->adc == 0));
gtk_widget_show(ps_ant_ext1);
gtk_grid_attach(GTK_GRID(grid), ps_ant_ext1, col, row, 1, 1);
g_signal_connect(ps_ant_ext1,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 3);
col++;
GtkWidget *ps_ant_ext2=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ps_ant_auto),"EXT2");
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext2), receiver[PS_RX_FEEDBACK]->feedback_antenna==4);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_ext2),
+ (receiver[PS_RX_FEEDBACK]->feedback_antenna==4) && (receiver[PS_RX_FEEDBACK]->adc == 0));
gtk_widget_show(ps_ant_ext2);
gtk_grid_attach(GTK_GRID(grid), ps_ant_ext2, col, row, 1, 1);
g_signal_connect(ps_ant_ext2,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 4);
+ col++;
+
+ if (n_adc > 1) {
+ // If there are two ADCs, we may choose to use the second ADC for PS_RX_FEEDBACK
+ GtkWidget *ps_ant_rx2=gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(ps_ant_auto),"RX2");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (ps_ant_rx2), receiver[PS_RX_FEEDBACK]->adc==1);
+ gtk_widget_show(ps_ant_rx2);
+ gtk_grid_attach(GTK_GRID(grid), ps_ant_rx2, col, row, 1, 1);
+ g_signal_connect(ps_ant_rx2,"toggled", G_CALLBACK(ps_ant_cb), (gpointer) (long) 99);
+ }
row++;
+
col=0;
feedback_l=gtk_label_new("Feedback Lvl");
gtk_widget_show(feedback_l);
// DL1YCF: This is not the default setting,
// but in my experience in behaves better in difficult situations.
- ints=8;
- spi=512;
- ampdelay=100e-9;
+// This is commented out because it is not recommended by the
+// WDSP manual.
+// ints=8;
+// spi=512;
+// ampdelay=100e-9;
SetPSIntsAndSpi(transmitter->id, ints, spi);
SetPSStabilize(transmitter->id, stbl);
int vox=0;
int CAT_cw_is_active=0;
int cw_key_hit=0;
+int n_adc=1;
int diversity_enabled=0;
double i_rotate[2]={1.0,1.0};
} else {
gtk_fixed_move(GTK_FIXED(fixed),sliders,0,y);
}
- gtk_widget_show_all(sliders); // DL1YCF this shows both C25 and Alex ATT/Preamp sliders
- att_type_changed(); // DL1YCF added here to hide the „wrong“ ones.
+ gtk_widget_show_all(sliders); // ... this shows both C25 and Alex ATT/Preamp sliders
+ att_type_changed(); // ... and this hides the „wrong“ ones.
} else {
if(sliders!=NULL) {
gtk_container_remove(GTK_CONTAINER(fixed),sliders);
radio->info.network.mac_address[5],
radio->info.network.interface_name);
-fprintf(stderr,"title: length=%d\n", (int)strlen(text));
+//fprintf(stderr,"title: length=%d\n", (int)strlen(text));
gtk_window_set_title (GTK_WINDOW (top_window), text);
break;
}
+ // Code moved here from rx_menu because n_adc is of general interest:
+ // Determine Number of ADCs.
+ switch(protocol) {
+ case ORIGINAL_PROTOCOL:
+ switch(device) {
+ case DEVICE_METIS:
+ n_adc=1; // No support for multiple MERCURY cards on a single ATLAS bus.
+ break;
+ case DEVICE_HERMES:
+ case DEVICE_HERMES_LITE:
+ n_adc=1;
+ break;
+ default:
+ n_adc=2;
+ break;
+ }
+ break;
+ case NEW_PROTOCOL:
+ switch(device) {
+ case NEW_DEVICE_ATLAS:
+ n_adc=1; // No support for multiple MERCURY cards on a single ATLAS bus.
+ break;
+ case NEW_DEVICE_HERMES:
+ case NEW_DEVICE_HERMES2:
+ case NEW_DEVICE_HERMES_LITE:
+ n_adc=1;
+ break;
+ default:
+ n_adc=2;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
adc_attenuation[0]=0;
adc_attenuation[1]=0;
rx_height=rx_height/receivers;
-fprintf(stderr,"Create %d receivers: height=%d\n",receivers,rx_height);
+//fprintf(stderr,"Create %d receivers: height=%d\n",receivers,rx_height);
for(i=0;i<RECEIVERS;i++) {
- receiver[i]=create_receiver(i, buffer_size, fft_size, display_width, updates_per_second, display_width, rx_height);
- setSquelch(receiver[i]);
+ // A receiver may be un-unsed upon program start, but can be activated through the radio menu later.
+ // Create it here with height 0. Make sure to call g_object_ref for this one as well,
+ // this cures the seg-faults observed when switching beteen 1 and 2 RX in the radio menu.
if(i<receivers) {
- receiver[i]->x=0;
- receiver[i]->y=y;
- gtk_fixed_put(GTK_FIXED(fixed),receiver[i]->panel,0,y);
- g_object_ref((gpointer)receiver[i]->panel);
+ receiver[i]=create_receiver(i, buffer_size, fft_size, display_width, updates_per_second, display_width, rx_height);
+ } else {
+ receiver[i]=create_receiver(i, buffer_size, fft_size, display_width, updates_per_second, display_width, 0 );
+ }
+ setSquelch(receiver[i]);
+ receiver[i]->x=0;
+ receiver[i]->y=y;
+ gtk_fixed_put(GTK_FIXED(fixed),receiver[i]->panel,0,y);
+ g_object_ref((gpointer)receiver[i]->panel);
+ if (i<receivers) {
set_displaying(receiver[i],1);
y+=rx_height;
} else {
active_receiver=receiver[0];
- fprintf(stderr,"Create transmitter\n");
+ //fprintf(stderr,"Create transmitter\n");
transmitter=create_transmitter(CHANNEL_TX, buffer_size, fft_size, updates_per_second, display_width, tx_height);
transmitter->x=0;
transmitter->y=VFO_HEIGHT;
//#endif
if(display_sliders) {
-fprintf(stderr,"create sliders\n");
+//fprintf(stderr,"create sliders\n");
sliders = sliders_init(display_width,SLIDERS_HEIGHT);
gtk_fixed_put(GTK_FIXED(fixed),sliders,0,y);
y+=SLIDERS_HEIGHT;
void radio_change_receivers(int r) {
+ // The button in the radio menu will call this function even if the
+ // number of receivers has not changed.
+ if (receivers == r) return;
fprintf(stderr,"radio_change_receivers: from %d to %d\n",receivers,r);
switch(r) {
case 1:
- if(receivers==2) {
- set_displaying(receiver[1],0);
- gtk_container_remove(GTK_CONTAINER(fixed),receiver[1]->panel);
- }
- receivers=1;
- break;
+ set_displaying(receiver[1],0);
+ gtk_container_remove(GTK_CONTAINER(fixed),receiver[1]->panel);
+ receivers=1;
+ break;
case 2:
- gtk_fixed_put(GTK_FIXED(fixed),receiver[1]->panel,0,0);
- set_displaying(receiver[1],1);
- receivers=2;
- break;
+ gtk_fixed_put(GTK_FIXED(fixed),receiver[1]->panel,0,0);
+ set_displaying(receiver[1],1);
+ receivers=2;
+ break;
}
reconfigure_radio();
active_receiver=receiver[0];
void radio_change_sample_rate(int rate) {
int i;
+
switch(protocol) {
case ORIGINAL_PROTOCOL:
- old_protocol_stop();
- for(i=0;i<receivers;i++) {
- receiver_change_sample_rate(receiver[i],rate);
- }
- old_protocol_set_mic_sample_rate(rate);
- old_protocol_run();
+ // The radio menu calls this function even if the sample rate
+ // has not changed. Do nothing in this case.
+ if (receiver[0]->sample_rate != rate) {
+ old_protocol_stop();
+ for(i=0;i<receivers;i++) {
+ receiver_change_sample_rate(receiver[i],rate);
+ }
+ old_protocol_set_mic_sample_rate(rate);
+ old_protocol_run();
#ifdef PURESIGNAL
- tx_set_ps_sample_rate(transmitter,rate);
+ tx_set_ps_sample_rate(transmitter,rate);
+ }
#endif
break;
#ifdef LIMESDR
}
pre_tune_mode=mode;
- // DL1YCF: in USB/DIGU/DSB, tune 1000 Hz above carrier
- // in LSB/DIGL, tune 1000 Hz below carrier
- // all other (CW, AM, FM): tune on carrier freq.
- // Note: do not look at CW sidetone freq here.
+ //
+ // in USB/DIGU/DSB, tune 1000 Hz above carrier
+ // in LSB/DIGL, tune 1000 Hz below carrier
+ // all other (CW, AM, FM): tune on carrier freq.
+ //
switch(mode) {
case modeLSB:
case modeDIGL:
void set_filter_type(int filter_type) {
int i;
- fprintf(stderr,"set_filter_type: %d\n",filter_type);
+ //fprintf(stderr,"set_filter_type: %d\n",filter_type);
for(i=0;i<RECEIVERS;i++) {
receiver[i]->low_latency=filter_type;
RXASetMP(receiver[i]->id, filter_type);
void set_filter_size(int filter_size) {
int i;
- fprintf(stderr,"set_filter_size: %d\n",filter_size);
+ //fprintf(stderr,"set_filter_size: %d\n",filter_size);
for(i=0;i<RECEIVERS;i++) {
receiver[i]->fft_size=filter_size;
RXASetNC(receiver[i]->id, filter_size);
extern int vox;
extern int CAT_cw_is_active;
extern int cw_key_hit;
+extern int n_adc;
extern int diversity_enabled;
extern double i_rotate[2];
if(filter_board==ALEX || filter_board==APOLLO) {
BAND *band=band_get_current_band();
- BANDSTACK_ENTRY* entry=bandstack_entry_get_current();
- setFrequency(entry->frequency);
+ // mode and filters have nothing to do with the filter board
+ //BANDSTACK_ENTRY* entry=bandstack_entry_get_current();
+ //setFrequency(entry->frequency);
//setMode(entry->mode);
- set_mode(active_receiver,entry->mode);
- FILTER* band_filters=filters[entry->mode];
- FILTER* band_filter=&band_filters[entry->filter];
- //setFilter(band_filter->low,band_filter->high);
- set_filter(active_receiver,band_filter->low,band_filter->high);
+ //set_mode(active_receiver,entry->mode);
+ //FILTER* band_filters=filters[entry->mode];
+ //FILTER* band_filter=&band_filters[entry->filter];
+ //set_filter(active_receiver,band_filter->low,band_filter->high);
if(active_receiver->id==0) {
set_alex_rx_antenna(band->alexRxAntenna);
set_alex_tx_antenna(band->alexTxAntenna);
gtk_fixed_put(GTK_FIXED(rx->panel),rx->panadapter,0,y);
} else {
// set the size
-fprintf(stderr,"reconfigure_receiver: panadapter set_size_request: width:%d height:%d\n",rx->width,height);
+//fprintf(stderr,"reconfigure_receiver: panadapter set_size_request: width:%d height:%d\n",rx->width,height);
gtk_widget_set_size_request(rx->panadapter, rx->width, height);
// move the current one
gtk_fixed_move(GTK_FIXED(rx->panel),rx->panadapter,0,y);
else if((strcmp(cmd_str,"KY")==0) && (zzid_flag == 0))
{
if (cw_busy < 0) cat_cw_seen=1;
- // DL1YCF:
+ //
// Hamlib produces timeout errors if we are busy here for
// seconds. Therefore we just move the data into a buffer
// that is processed by a separate thread.
// - if we can accept new data (buffer space available) : "KY0;"
// - if buffer is full: "KY1;"
//
- // Note: cw_buse == -1 indicates a "purge KY" situation, where
+ // Note: cw_busy == -1 indicates a "purge KY" situation, where
// all KY commands are accepted and data is discared silently
// In this case cw_busy is left untouched here.
+ //
if (len <= 2) {
if (cw_busy == 1) {
send_resp(client_sock,"KY1;");
}
//
-// DL1YCF:
// possible the device has been changed:
// call audo_close_output with old device, audio_open_output with new one
//
}
}
- // Number of ADCs.
- int n_adc=1;
- switch(protocol) {
- case ORIGINAL_PROTOCOL:
- switch(device) {
- case DEVICE_METIS:
- n_adc=1; // No support for multiple MERCURY cards on a single ATLAS bus.
- break;
- case DEVICE_HERMES:
- case DEVICE_HERMES_LITE:
- n_adc=1;
- break;
- default:
- n_adc=2;
- break;
- }
- break;
- case NEW_PROTOCOL:
- switch(device) {
- case NEW_DEVICE_ATLAS:
- n_adc=1; // No support for multiple MERCURY cards on a single ATLAS bus.
- break;
- case NEW_DEVICE_HERMES:
- case NEW_DEVICE_HERMES2:
- case NEW_DEVICE_HERMES_LITE:
- n_adc=1;
- break;
- default:
- n_adc=2;
- break;
- }
- break;
- default:
- break;
- }
-
// If there is more than one ADC, let the user associate an ADC
- // with the current receiver. Note: for PURESIGNAL, ADC0 has to
- // be associated with the first receiver.
+ // with the current receiver.
if(n_adc>1) {
for(i=0;i<n_adc;i++) {
sprintf(label,"ADC-%d",i);
static void c25_att_combobox_changed(GtkWidget *widget, gpointer data) {
int val = atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)));
if (active_receiver->id == 0) {
- //DL1YCF: this button is only valid for the first receiver
- // store attenuation, such that in meter.c the correct level is displayed
+ // this button is only valid for the first receiver
+ // store attenuation, such that in meter.c the correct level is displayed
adc_attenuation[active_receiver->adc] = 12*val;
set_alex_attenuation(val);
} else {
static void c25_preamp_combobox_changed(GtkWidget *widget, gpointer data) {
int val = atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(widget)));
if (active_receiver->id == 0) {
- //DL1YCF: This button is only valid for the first receiver
- // dither and preamp are "misused" to store the PreAmp value.
- // this has to be exploited in meter.c
+ // This button is only valid for the first receiver
+ // dither and preamp are "misused" to store the PreAmp value.
+ // this has to be exploited in meter.c
active_receiver->dither = (val >= 2); // second preamp ON
active_receiver->preamp = (val >= 1); // first preamp ON
} else{
sprintf(name,"transmitter.%d.feedback",tx->id);
sprintf(value,"%d",tx->feedback);
setProperty(name,value);
+ sprintf(name,"transmitter.%d.attenuation",tx->id);
+ sprintf(value,"%d",tx->attenuation);
+ setProperty(name,value);
#endif
sprintf(name,"transmitter.%d.ctcss",tx->id);
sprintf(value,"%d",tx->ctcss);
sprintf(name,"transmitter.%d.feedback",tx->id);
value=getProperty(name);
if(value) tx->feedback=atoi(value);
+ sprintf(name,"transmitter.%d.attenuation",tx->id);
+ value=getProperty(name);
+ if(value) tx->attenuation=atoi(value);
#endif
sprintf(name,"transmitter.%d.ctcss",tx->id);
value=getProperty(name);
#ifdef PURESIGNAL
void tx_set_ps(TRANSMITTER *tx,int state) {
- tx->puresignal=state;
if(state) {
+ tx->puresignal=1;
SetPSControl(tx->id, 0, 0, 1, 0);
} else {
SetPSControl(tx->id, 1, 0, 0, 0);
+ // wait a moment for PS to shut down
+ usleep(100000);
+ tx->puresignal=0;
}
vfo_update();
}
void tx_set_twotone(TRANSMITTER *tx,int state) {
+ fprintf(stderr,"TX TWO TONE new state=%d\n", state);
transmitter->twotone=state;
if(state) {
- // DL1YCF: set frequencies and levels
+ // set frequencies and levels
switch(tx->mode) {
case modeCWL:
case modeLSB:
//
void cw_hold_key(int state) {
if (state) {
- cw_key_down = 4800000; // up to 100 sec
+ cw_key_down = 480000; // up to 10 sec
} else {
cw_key_down = 0;
}
}
-// DL1YCF:
+// Sine tone generator:
// somewhat improved, and provided two siblings
// for generating side tones simultaneously on the
// HPSDR board and local audio.
static void comp_enable_cb(GtkWidget *widget, gpointer data) {
transmitter->compressor=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ g_idle_add(ext_vfo_update, NULL);
}
static void comp_cb(GtkWidget *widget, gpointer data) {
transmitter_set_compressor_level(transmitter,gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
+ g_idle_add(ext_vfo_update, NULL);
}
static void tx_spin_low_cb (GtkWidget *widget, gpointer data) {
static gboolean emp_cb (GtkWidget *widget, gpointer data) {
pre_emphasize=gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
tx_set_pre_emphasize(transmitter,pre_emphasize);
- // DL1YCF added return statement to make the compiler happy.
- // however I am unsure about the correct return value.
- // I would have coded this as a void function.
return FALSE;
}
} else {
vfo_move(-step);
}
- // DL1YCF added return statement to make the compiler happy.
- // however I am unsure about the correct return value.
- // I would have coded this as a void function.
return FALSE;
}
}
cairo_show_text(cr, temp_text);
- // DL1YCF: in what follows, we want to display the VFO frequency
+ // In what follows, we want to display the VFO frequency
// on which we currently transmit a signal with red colour.
// If it is out-of-band, we display "Out of band" in red.
// Frequencies we are not transmitting on are displayed in green
cairo_set_font_size(cr, 12);
cairo_show_text(cr, temp_text);
+ // NB and NB2 are mutually exclusive, therefore
+ // they are put to the same place
cairo_move_to(cr, 150, 50);
if(active_receiver->nb) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
- } else {
- cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
- }
- cairo_show_text(cr, "NB");
-
- cairo_move_to(cr, 175, 50);
- if(active_receiver->nb2) {
+ cairo_show_text(cr, "NB");
+ } else if (active_receiver->nb2) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
- } else {
+ cairo_show_text(cr, "NB2");
+ } else {
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
+ cairo_show_text(cr, "NB");
}
- cairo_show_text(cr, "NB2");
- cairo_move_to(cr, 200, 50);
+ // NR and NR2 are mutually exclusive
+ cairo_move_to(cr, 180, 50);
if(active_receiver->nr) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
- } else {
- cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
- }
- cairo_show_text(cr, "NR");
-
- cairo_move_to(cr, 225, 50);
- if(active_receiver->nr2) {
+ cairo_show_text(cr, "NR");
+ } else if (active_receiver->nr2) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
- } else {
+ cairo_show_text(cr, "NR2");
+ } else {
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
+ cairo_show_text(cr, "NR2");
}
- cairo_show_text(cr, "NR2");
- cairo_move_to(cr, 250, 50);
+ cairo_move_to(cr, 210, 50);
if(active_receiver->anf) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
} else {
}
cairo_show_text(cr, "ANF");
- cairo_move_to(cr, 275, 50);
+ cairo_move_to(cr, 240, 50);
if(active_receiver->snb) {
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
} else {
}
cairo_show_text(cr, "SNB");
- cairo_move_to(cr, 350, 50);
+ cairo_move_to(cr, 300, 50);
switch(active_receiver->agc) {
case AGC_OFF:
cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
break;
case AGC_MEDIUM:
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
- cairo_show_text(cr, "AGC MEDIUM");
+ cairo_show_text(cr, "AGC MED");
break;
case AGC_FAST:
cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
break;
}
+ //
+ // Since we can now change it by a MIDI controller,
+ // we should display the compressor (level)
+ //
+ cairo_move_to(cr, 400, 50);
+ if (transmitter->compressor) {
+ sprintf(temp_text,"CMPR %d dB",(int) transmitter->compressor_level);
+ cairo_set_source_rgb(cr, 1.0, 1.0, 0.0);
+ cairo_show_text(cr, temp_text);
+ } else {
+ cairo_set_source_rgb(cr, 0.7, 0.7, 0.7);
+ cairo_show_text(cr, "CMPR OFF");
+ }
+
int s=0;
while(steps[s]!=step && steps[s]!=0) {
s++;
g_idle_add(level_update,NULL);
usleep(100000); // 100ms
}
- // DL1YCF added return statement to make compilers happy.
return NULL;
}