From 34bb771ca43c3a3ba9e025bb5b873e457a2ba581 Mon Sep 17 00:00:00 2001 From: c vw Date: Thu, 30 May 2019 11:59:08 +0200 Subject: [PATCH] a) fixed seg-faults associated with RX2 panel b) finalized MIDI c) reworked PURESIGNAL ADC associations --- README.MIDI | 17 ++++-- audio.c | 4 +- ext.c | 10 ++++ ext.h | 2 + frequency.c | 3 +- hpsdrsim.c | 141 +++++++++++++++++++++++------------------------- main.c | 32 +++++------ midi.h | 62 ++++++++++++--------- midi.inp | 71 ++++++++++++++++++------ midi2.c | 57 +++++++++++++------- midi3.c | 114 +++++++++++++++++++++++++++++++++++++-- old_protocol.c | 71 ++++++++++++------------ portaudio.c | 11 ++-- ps_menu.c | 72 +++++++++++++++++++------ radio.c | 120 +++++++++++++++++++++++++++++------------ radio.h | 1 + radio_menu.c | 14 ++--- receiver.c | 2 +- rigctl.c | 5 +- rx_menu.c | 40 +------------- sliders.c | 10 ++-- transmitter.c | 18 +++++-- tx_menu.c | 5 +- tx_panadapter.c | 3 -- vfo.c | 57 +++++++++++--------- vox_menu.c | 1 - 26 files changed, 603 insertions(+), 340 deletions(-) diff --git a/README.MIDI b/README.MIDI index 4f248df..57a31f9 100644 --- a/README.MIDI +++ b/README.MIDI @@ -136,33 +136,44 @@ drive could be used with a knob (CTRL but no WHEEL), with a wheel 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. diff --git a/audio.c b/audio.c index cf49fee..4c98c8d 100644 --- a/audio.c +++ b/audio.c @@ -18,8 +18,8 @@ */ // -// 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 diff --git a/ext.c b/ext.c index c86cd5c..daacc6d 100644 --- a/ext.c +++ b/ext.c @@ -179,6 +179,16 @@ int ext_vfo_a_swap_b(void *data) { 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; diff --git a/ext.h b/ext.h index 070a215..c3c9710 100644 --- a/ext.h +++ b/ext.h @@ -63,6 +63,8 @@ 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_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); diff --git a/frequency.c b/frequency.c index 8c9eca2..d69fe76 100644 --- a/frequency.c +++ b/frequency.c @@ -436,7 +436,7 @@ char* getFrequencyInfo(long long frequency,int filter_low,int filter_high) { 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. @@ -445,6 +445,7 @@ char* getFrequencyInfo(long long frequency,int filter_low,int filter_high) { // 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; diff --git a/hpsdrsim.c b/hpsdrsim.c index 6ecf73c..c267d14 100644 --- a/hpsdrsim.c +++ b/hpsdrsim.c @@ -13,24 +13,33 @@ * * 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). @@ -131,7 +140,7 @@ static int lna6m=-1; 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; @@ -262,7 +271,7 @@ int main(int argc, char *argv[]) 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; @@ -855,6 +864,12 @@ void process_ep2(uint8_t *frame) 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: @@ -1009,81 +1024,61 @@ void *handler_ep6(void *arg) pointer += 8; memset(pointer, 0, 504); for (j=0; j> 16) & 0xFF; *pointer++ = (myisample >> 8) & 0xFF; *pointer++ = (myisample >> 0) & 0xFF; diff --git a/main.c b/main.c index ffb0242..55c9708 100644 --- a/main.c +++ b/main.c @@ -152,7 +152,7 @@ static int init(void *data) { char wisdom_directory[1024]; int rc; - fprintf(stderr,"init\n"); + //fprintf(stderr,"init\n"); audio_get_cards(); @@ -221,17 +221,17 @@ fprintf(stderr,"width=%d height=%d\n", display_width, display_height); 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"); @@ -253,41 +253,41 @@ fprintf(stderr,"full screen\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); /* @@ -315,12 +315,12 @@ int main(int argc,char **argv) { 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; } diff --git a/midi.h b/midi.h index 98a6191..50915a7 100644 --- a/midi.h +++ b/midi.h @@ -44,30 +44,41 @@ // // 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 }; // @@ -153,9 +164,9 @@ struct desc { 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 { @@ -186,7 +197,8 @@ 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. +// 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); diff --git a/midi.inp b/midi.inp index 0e6ebca..cb0ddce 100644 --- a/midi.inp +++ b/midi.inp @@ -3,31 +3,72 @@ # # 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 diff --git a/midi2.c b/midi2.c index 0d4d300..9145137 100644 --- a/midi2.c +++ b/midi2.c @@ -63,7 +63,6 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) { 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; @@ -75,6 +74,12 @@ void NewMidiEvent(enum MIDIevent event, int channel, int note, int val) { 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); + } } /* @@ -86,27 +91,38 @@ 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"}, + { 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} }; @@ -118,7 +134,10 @@ static enum MIDIaction keyword2action(char *s) { 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 */ @@ -170,8 +189,8 @@ void MIDIstartup() { 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; diff --git a/midi3.c b/midi3.c index 8e0c33f..d654dca 100644 --- a/midi3.c +++ b/midi3.c @@ -18,6 +18,7 @@ #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) { @@ -33,7 +34,9 @@ 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) { @@ -150,18 +153,23 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) { } } 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; @@ -196,6 +204,8 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) { } 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); @@ -227,8 +237,104 @@ void DoTheMidi(enum MIDIaction action, enum MIDItype type, int val) { } 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); } } diff --git a/old_protocol.c b/old_protocol.c index 3ef7d43..b4a65a3 100644 --- a/old_protocol.c +++ b/old_protocol.c @@ -565,7 +565,6 @@ static void process_ozy_input_buffer(unsigned char *buffer) { 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. @@ -849,7 +848,6 @@ void ozy_send_buffer() { 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. @@ -957,22 +955,23 @@ void ozy_send_buffer() { // 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 @@ -1051,8 +1050,8 @@ void ozy_send_buffer() { 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) { @@ -1178,7 +1177,7 @@ void ozy_send_buffer() { 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 { @@ -1193,7 +1192,7 @@ void ozy_send_buffer() { 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; } @@ -1209,33 +1208,20 @@ void ozy_send_buffer() { // 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); @@ -1282,8 +1268,19 @@ void ozy_send_buffer() { 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 @@ -1453,7 +1450,7 @@ static void metis_restart() { 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 { diff --git a/portaudio.c b/portaudio.c index bd0b987..a1ece79 100644 --- a/portaudio.c +++ b/portaudio.c @@ -1,9 +1,12 @@ #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 diff --git a/ps_menu.c b/ps_menu.c index 5008ff0..5213ed3 100644 --- a/ps_menu.c +++ b/ps_menu.c @@ -106,15 +106,8 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d } // -// -// 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]; @@ -264,9 +257,24 @@ static int info_thread(gpointer arg) { 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; + } } } @@ -276,6 +284,7 @@ static void enable_cb(GtkWidget *widget, gpointer data) { 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; } @@ -283,6 +292,13 @@ static void auto_cb(GtkWidget *widget, gpointer data) { } 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); } @@ -297,7 +313,6 @@ static void feedback_cb(GtkWidget *widget, gpointer data) { } static void reset_cb(GtkWidget *widget, gpointer data) { - transmitter->attenuation=0; SetPSControl(transmitter->id, 1, 0, 0, 0); } @@ -398,6 +413,14 @@ void ps_menu(GtkWidget *parent) { 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); @@ -405,26 +428,39 @@ void ps_menu(GtkWidget *parent) { 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); @@ -518,9 +554,11 @@ void ps_menu(GtkWidget *parent) { // 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); diff --git a/radio.c b/radio.c index 74a4445..b646170 100644 --- a/radio.c +++ b/radio.c @@ -294,6 +294,7 @@ double vox_hang=250.0; 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}; @@ -327,8 +328,8 @@ void reconfigure_radio() { } 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); @@ -421,7 +422,7 @@ void start_radio() { 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); @@ -482,6 +483,41 @@ fprintf(stderr,"title: length=%d\n", (int)strlen(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; @@ -531,15 +567,22 @@ fprintf(stderr,"title: length=%d\n", (int)strlen(text)); 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;ix=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 (ix=0; transmitter->y=VFO_HEIGHT; @@ -604,7 +647,7 @@ fprintf(stderr,"Create %d receivers: height=%d\n",receivers,rx_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; @@ -664,20 +707,21 @@ void disable_rigctl() { 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]; @@ -688,16 +732,21 @@ void radio_change_receivers(int r) { void radio_change_sample_rate(int rate) { int i; + switch(protocol) { case ORIGINAL_PROTOCOL: - old_protocol_stop(); - for(i=0;isample_rate != rate) { + old_protocol_stop(); + for(i=0;ilow_latency=filter_type; RXASetMP(receiver[i]->id, filter_type); @@ -1536,7 +1586,7 @@ void set_filter_type(int 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;ifft_size=filter_size; RXASetNC(receiver[i]->id, filter_size); diff --git a/radio.h b/radio.h index 8a7656d..8e8c349 100644 --- a/radio.h +++ b/radio.h @@ -244,6 +244,7 @@ extern double vox_hang; 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]; diff --git a/radio_menu.c b/radio_menu.c index 05cb830..9d40fdb 100644 --- a/radio_menu.c +++ b/radio_menu.c @@ -101,14 +101,14 @@ static void load_filters(void) { 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); diff --git a/receiver.c b/receiver.c index bdaf835..9a1ed09 100644 --- a/receiver.c +++ b/receiver.c @@ -480,7 +480,7 @@ fprintf(stderr,"reconfigure_receiver: panadapter_init: width:%d height:%d\n",rx- 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); diff --git a/rigctl.c b/rigctl.c index 81b40dc..052218f 100644 --- a/rigctl.c +++ b/rigctl.c @@ -2216,7 +2216,7 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { 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. @@ -2228,9 +2228,10 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) { // - 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;"); diff --git a/rx_menu.c b/rx_menu.c index 54f7e0e..4cff08f 100644 --- a/rx_menu.c +++ b/rx_menu.c @@ -112,7 +112,6 @@ static void mute_radio_cb(GtkWidget *widget, gpointer data) { } // -// DL1YCF: // possible the device has been changed: // call audo_close_output with old device, audio_open_output with new one // @@ -290,45 +289,8 @@ void rx_menu(GtkWidget *parent) { } } - // 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;iid == 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 { @@ -245,9 +245,9 @@ static void c25_att_combobox_changed(GtkWidget *widget, gpointer data) { 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{ diff --git a/transmitter.c b/transmitter.c index 465ec9f..00d5e4e 100644 --- a/transmitter.c +++ b/transmitter.c @@ -173,6 +173,9 @@ void transmitter_save_state(TRANSMITTER *tx) { 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); @@ -259,6 +262,9 @@ void transmitter_restore_state(TRANSMITTER *tx) { 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); @@ -1087,19 +1093,23 @@ void tx_set_displaying(TRANSMITTER *tx,int state) { #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: @@ -1130,13 +1140,13 @@ void tx_set_ps_sample_rate(TRANSMITTER *tx,int rate) { // 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. diff --git a/tx_menu.c b/tx_menu.c index 7e08fc9..895bce0 100644 --- a/tx_menu.c +++ b/tx_menu.c @@ -59,10 +59,12 @@ static gboolean delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_d 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) { @@ -160,9 +162,6 @@ static void local_input_changed_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; } diff --git a/tx_panadapter.c b/tx_panadapter.c index 7a8d590..e9690f7 100644 --- a/tx_panadapter.c +++ b/tx_panadapter.c @@ -164,9 +164,6 @@ tx_panadapter_scroll_event_cb (GtkWidget *widget, } 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; } diff --git a/vfo.c b/vfo.c index 20ead0c..997b4ec 100644 --- a/vfo.c +++ b/vfo.c @@ -687,7 +687,7 @@ void vfo_update() { } 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 @@ -742,39 +742,34 @@ void vfo_update() { 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 { @@ -782,7 +777,7 @@ void vfo_update() { } 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 { @@ -790,7 +785,7 @@ void vfo_update() { } 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); @@ -806,7 +801,7 @@ void vfo_update() { 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); @@ -814,6 +809,20 @@ void vfo_update() { 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++; diff --git a/vox_menu.c b/vox_menu.c index e14d5f2..ff418a5 100644 --- a/vox_menu.c +++ b/vox_menu.c @@ -91,7 +91,6 @@ static gpointer level_thread(gpointer arg) { g_idle_add(level_update,NULL); usleep(100000); // 100ms } - // DL1YCF added return statement to make compilers happy. return NULL; } -- 2.45.2