]> git.rkrishnan.org Git - pihpsdr.git/commitdiff
Finalized CW stuff. Local CW now should work.
authorc vw <dl1ycf@darc.de>
Thu, 12 Jul 2018 14:02:06 +0000 (16:02 +0200)
committerc vw <dl1ycf@darc.de>
Thu, 12 Jul 2018 14:02:06 +0000 (16:02 +0200)
ext.c
iambic.c
old_protocol.c
radio.c
radio.h
rigctl.c
transmitter.c
transmitter.h

diff --git a/ext.c b/ext.c
index a7cbb55bd6147f0f776bb9e227dceec1cc0bb573..331883dd6d21a36ec115c6a397fb0e6e4f6e036d 100644 (file)
--- a/ext.c
+++ b/ext.c
@@ -141,11 +141,15 @@ int ext_radio_change_sample_rate(void *data) {
   return 0;
 }
 
+// DL1YCF: because of the new CW algorithm,
+//         this function is no longer used
 int ext_cw_setup() {
   radio_cw_setup();
   return 0;
 }
 
+// DL1YCF: because of the new CW algorithm,
+//         this function is no longer used
 int ext_cw_key(void *data) {
   radio_cw_key((uintptr_t)data);
   return 0;
index 71f250297c6f397ceb1949b66822b30fb5464fd5..5cd0307c1adf070cc8db7f8e565150276a7d1490 100644 (file)
--- a/iambic.c
+++ b/iambic.c
@@ -158,6 +158,10 @@ void keyer_update() {
 void keyer_event(int gpio, int level) {
     int state = (level == 0);
 
+    // This is for aborting CAT CW messages if the key is hit.
+    if (state) {
+       cw_key_hit = 1;
+    }
     if (gpio == CWL_BUTTON)
         kcwl = state;
     else  // CWR_BUTTON
@@ -180,21 +184,26 @@ void clear_memory() {
 
 void set_keyer_out(int state) {
     if (keyer_out != state) {
+       // DL1YCF: Shouln't one activate PTT if not yet done?
+       //         At the moment, it is required to *manually* activate
+       //         MOX before starting local CW.
         keyer_out = state;
         if(protocol==NEW_PROTOCOL) schedule_high_priority(9);
                fprintf(stderr,"set_keyer_out keyer_out= %d\n", keyer_out);
-        if (state)
-            if (SIDETONE_GPIO)
+        if (state) {
+           // DL1YCF: we must call cw_hold_key in *any* case, else no
+           //         CW signal will be produced. We certainly do not
+           //         want to produce a side tone *only*.
+            if (SIDETONE_GPIO) {
                 softToneWrite (SIDETONE_GPIO, cw_keyer_sidetone_frequency);
-            else {
-                               cw_sidetone_mute(1);
-            }
-        else
-            if (SIDETONE_GPIO)
+           }
+           cw_hold_key(1); // this starts a CW pulse in transmitter.c
+        } else {
+            if (SIDETONE_GPIO) {
                 softToneWrite (SIDETONE_GPIO, 0);
-            else  {
-                               cw_sidetone_mute(0);
-            }
+           }
+           cw_hold_key(0); // this stops a CW pulse in transmitter.c
+        }
     }
 }
 
index 3c26cae7b7028431c9832918fc663db9627aebbe..99814a0108de95479b221f6b8a022a21fc10c1df 100644 (file)
@@ -1218,21 +1218,20 @@ void ozy_send_buffer() {
   } else {
     mode=vfo[0].mode;
   }
-  if(mode==modeCWU || mode==modeCWL) {
+  if (isTransmitting()) {
+    if(mode==modeCWU || mode==modeCWL) {
 //
-//  The default is doing 'external CW', that is,
-//  CW is entirely done on the HPSDR board. In this
-//  case, we should not set MOX, the PTT switching on
-//  the HPSDR board is done by the board itself.
+//    If CW is done on the HPSDR board, we should not set
+//    the MOX bit, everything is done in the FPGA.
 //
-//  However, if we are doing local CW or tuning,
-//  we must put the SDR into TX mode.
+//    However, if we are doing CAT CW, local CW or tuning,
+//    we must put the SDR into TX mode.
 //
-    if(isTransmitting() && (tune || local_cw_is_active)) {
-      output_buffer[C0]|=0x01;
-    }
-  } else {
-    if(isTransmitting()) {
+      if(tune || CAT_cw_is_active || !cw_keyer_internal) {
+        output_buffer[C0]|=0x01;
+      }
+    } else {
+      // not doing CW? set MOX in any case.
       output_buffer[C0]|=0x01;
     }
   }
diff --git a/radio.c b/radio.c
index 854f0537f82fa32eeed57652b76d28d55ada8554..090092a34edd89600e817b9204fecac09dceb329 100644 (file)
--- a/radio.c
+++ b/radio.c
@@ -288,7 +288,7 @@ double vox_threshold=0.001;
 double vox_gain=10.0;
 double vox_hang=250.0;
 int vox=0;
-int local_cw_is_active=0;
+int CAT_cw_is_active=0;
 int cw_key_hit=0;
 
 int diversity_enabled=0;
@@ -899,6 +899,8 @@ int getTune() {
   return tune;
 }
 
+// DL1YCF: because of the new CW algorithm,
+//         this function is no longer used
 void radio_cw_setup() {
   int mode=vfo[VFO_A].mode;;
   if(split) {
@@ -912,6 +914,8 @@ void radio_cw_setup() {
   SetTXAPostGenToneMag(transmitter->id,0.99999);
 }
 
+// DL1YCF: because of the new CW algorithm,
+//         this function is no longer used
 void radio_cw_key(int state) {
   SetTXAPostGenRun(transmitter->id,state);
 }
diff --git a/radio.h b/radio.h
index b455aec66bf47525eed9a116600e6248f8ded562..94215346d869d3f1664d769aa1addf68abaca558 100644 (file)
--- a/radio.h
+++ b/radio.h
@@ -244,7 +244,7 @@ extern double vox_threshold;
 extern double vox_gain;
 extern double vox_hang;
 extern int vox;
-extern int local_cw_is_active;
+extern int CAT_cw_is_active;
 extern int cw_key_hit;
 
 extern int diversity_enabled;
index 599b686f1dc1df61b2a723bf42f41e06dad9440d..a3305a27a87e85870d7c8e544684865a55bd5871 100644 (file)
--- a/rigctl.c
+++ b/rigctl.c
@@ -495,11 +495,10 @@ static gpointer rigctl_cw_thread(gpointer data)
     dashlen = (dotlen * 3 * cw_keyer_weight) / 50L;
     dotsamples = 57600 / cw_keyer_speed;
     dashsamples = (3456 * cw_keyer_weight) / cw_keyer_speed;
-    local_cw_is_active=1;
+    CAT_cw_is_active=1;
     if (!mox) {
        // activate PTT
         g_idle_add(ext_ptt_update ,(gpointer)1);
-        g_idle_add(ext_cw_key     ,(gpointer)1);
        // have to wait until it is really there
         while (!mox) usleep(50000L);
        // some extra time to settle down
@@ -514,8 +513,7 @@ static gpointer rigctl_cw_thread(gpointer data)
        // CW transmission has been aborted, either due to manually
        // removing MOX, changing the mode to non-CW, or because a CW key has been hit.
        // Do not remove PTT if we abort CAT CW because a CW key has been hit.
-       local_cw_is_active=0;
-       g_idle_add(ext_cw_key     ,(gpointer)0);
+       CAT_cw_is_active=0;
        // If an external CW key has been hit, we continue in TX mode
        // doing CW manually. Otherwise, switch PTT off.
        if (!cw_key_hit) {
@@ -536,8 +534,7 @@ static gpointer rigctl_cw_thread(gpointer data)
       // Otherwise remove PTT and wait for next CAT
       // CW command.
       if (cw_busy) continue;
-      local_cw_is_active=0;
-      g_idle_add(ext_cw_key     ,(gpointer)0);
+      CAT_cw_is_active=0;
       g_idle_add(ext_ptt_update ,(gpointer)0);
     }
   }
@@ -547,7 +544,6 @@ static gpointer rigctl_cw_thread(gpointer data)
   // of a transmission
   rigctl_cw_thread_id = NULL;
   cw_busy=0;
-  g_idle_add(ext_cw_key     ,(gpointer)0);
   g_idle_add(ext_ptt_update ,(gpointer)0);
   return NULL;
 }
@@ -1754,7 +1750,6 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
                                                      sprintf(msg,"ZZFB%011lld;",vfo[VFO_B].frequency);
                                                   }
                                                }
-                                               fprintf(stderr,"RIGCTL: FB=%s\n",msg);
                                                send_resp(client_sock,msg);
                                             }
                                          }
@@ -2175,11 +2170,7 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
                                            // - Note further that the space immediately following "KY" is *not*
                                            //   part of the message.
                                            if (!cw_busy && len > 3) {
-                                               // A CW text will be sent. We have to call cw_setup here because
-                                               // TUNE changes the WDSP side tone frequency, and in this case we have
-                                               // to re-adjust it.
-                                               g_idle_add(ext_cw_setup,NULL);    // Initialize for external transmit
-                                               // Copy data to buffer
+                                               // A CW text will be sent. Copy incoming data to buffer
                                                strncpy(cw_buf, cmd_input+3, 29);
                                                // Kenwood protocol allows for at most 24 characters, so
                                                // this seems pure paranoia -- but who knows?
@@ -3223,13 +3214,8 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
         else if(strcmp(cmd_str,"SM")==0) {  
                                             // PiHPSDR - ZZSM - Read SMeter - same range as TS 2000
                                             // TI-2000 - SM - Read SMETER
-                                            // SMx;  x=0 RX1, x=1 RX2 
-                                            // meter is in dbm - value will be 0<260
-                                            // Translate to 00-30 for main, 0-15 fo sub
-                                            // Resp= SMxAABB; 
-                                            //  Received range from the SMeter can be -127 for S0, S9 is -73, and S9+60=-13.;
-                                            //  PowerSDR returns S9=0015 code. 
-                                            //  Let's make S9 half scale or a value of 70.  
+                                            // SM0:  main receiver, SM1: sub receiver
+                                            // Resp= SM0xxxx or SM1yyyy; xxxx between 0000 and 0030, yyyy between 0000 and 0015.
                                             double level=0.0;
 
                                             int r=0;
@@ -3242,23 +3228,38 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
                                             }
                                             level = GetRXAMeter(receiver[r]->id, smeter); 
 
-                                            // Determine how high above 127 we are..making a range of 114 from S0 to S9+60db
-                                            // 5 is a fugdge factor that shouldn't be there - but seems to get us to S9=SM015
-                                           // DL1YCF replaced abs by fabs, and changed 127 to floating point constant
-                                            level =  fabs(127.0+(level + (double) adc_attenuation[receiver[r]->adc]))+5;
-                                         
-                                            // Clip the value just in case
-                                            if(cmd_input[2] == '0') { 
-                                               new_level = (int) ((level * 30.0)/114.0);
-                                               // Do saturation check
-                                               if(new_level < 0) { new_level = 0; }
-                                               if(new_level > 30) { new_level = 30;}
-                                            } else { //Assume we are using sub receiver where range is 0-15
-                                               new_level = (int) ((level * 15.0)/114.0);
-                                               // Do saturation check
-                                               if(new_level < 0) { new_level = 0; }
-                                               if(new_level > 15) { new_level = 15;}
-                                            }
+                                            if(r == 0) { 
+                                               // DL1YCF: In the "main" bar graph, each bar counts 4 dB.
+                                               // S9+60 has 30 bars, S9 has 15 bars, S7 has 12 bars, and so on
+                                               // since S9 is about -73 dBm, the correct formula for converting
+                                               // dBm to bars is 15 + (level+73)/4 or 0.25*level + 33.25.
+                                               // The problem is now that S1 has 3 bars, such that 0 bars would
+                                               // correspond to S0 - 6dB. If one assumes that S0 corresponds
+                                               // to 0 bars, then it follows that the slope is 2 dB per bar
+                                               // below S1. This assumption is used in hamlib, and thus followed here.
+                                               if (level <= -121.0) {
+                                                   new_level = (int) round(0.50*level+63.50);   // valid up to S1 = -48 dBm
+                                               } else {
+                                                   new_level = (int) round(0.25*level+33.25);   // valid if at least S1
+                                               }
+                                               // Clip
+                                               if(new_level < 0) { new_level = 0; }
+                                               if(new_level > 30) { new_level = 30;}
+                                            } else {
+                                               // DL1YCF: studying the pictures in the TS2000 manual,
+                                               // for the "sub" display we have 0-9 small bars for S0 -S9, and in addition 1-6
+                                               // large bars for S9+10, ..., S9+60. So the slope is 6 dB per par
+                                               // up to S9, and 10 dB per bar beyond.
+                                               if (level <= -73.0) {
+                                                   new_level = (int) round(0.16667*level+21.16667);   // valid up to S9
+                                               } else {
+                                                   new_level = (int) round(0.1*level+16.3);           // valid if at least S9
+                                               }
+                                               // Clip
+                                               if(new_level < 0) { new_level = 0; }
+                                               if(new_level > 15) { new_level = 15;}
+                                           }
+
                                             if(zzid_flag == 0) {
                                                sprintf(msg,"SM%1c%04d;",
                                                            cmd_input[2],new_level);
@@ -3517,7 +3518,6 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
                                                g_idle_add(ext_mox_update,(gpointer)(long)mox_state); 
                                                g_idle_add(ext_vfo_update,NULL);
                                             } else {
-                                              g_idle_add(ext_cw_setup,NULL);    // Initialize for external transmit
                                               g_idle_add(ext_mox_update,(gpointer)(long)1); // Turn on External MOX
                                             }
                                          }
@@ -3682,17 +3682,19 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
                                              }
                                          }
         else if((strcmp(cmd_str,"XC")==0) && (zzid_flag == 1)) {  
-                                            // PiHPSDR - ZZXC  - Turn off External MOX- 
-                                            g_idle_add(ext_mox_update,(gpointer)(long)0); // Turn on xmitter (set Mox)
+                                            // PiHPSDR - ZZXC  - Terminate "CW" via ZZXO/ZZXT (Key up and MOX off)
+                                           cw_hold_key(0);
+                                            g_idle_add(ext_mox_update,(gpointer)(long)0);
+                                           CAT_cw_is_active=0;
                                          }
         else if((strcmp(cmd_str,"XI")==0) && (zzid_flag == 1)) {  
-                                            // PiHPSDR - ZZXI - Initialize the transmitter for external CW
-                                            g_idle_add(ext_cw_setup,NULL);    // Initialize for external transmit
-                                            g_idle_add(ext_mox_update,(gpointer)(long)1); // Turn on External MOX
+                                            // PiHPSDR - ZZXI - Prepeare for "CW" via ZZXO/ZZXT (MOX on)
+                                           CAT_cw_is_active=1;
+                                            g_idle_add(ext_mox_update,(gpointer)(long)1);
                                          }
        else if((strcmp(cmd_str,"XO")==0) && (zzid_flag == 1)) {  
-                                            // PiHPSDR - ZZXT - Turn CW Note off when in CW mode
-                                            g_idle_add(ext_cw_key, (gpointer)(long)0);
+                                            // PiHPSDR - ZZXO - Turn CW Note off when in CW mode
+                                           cw_hold_key(0);
                                          }
         else if(strcmp(cmd_str,"XT")==0) {  
                                            if(zzid_flag == 0 ) {
@@ -3703,7 +3705,7 @@ void parse_cmd ( char * cmd_input,int len,int client_sock) {
                                              }
                                            } else {
                                             // PiHPSDR - ZZXT - Turn CW Note on when in CW mode
-                                            g_idle_add(ext_cw_key, (gpointer)(long)1);
+                                           cw_hold_key(1);
                                            }
                                          }
         else if((strcmp(cmd_str,"XX")==0) && (zzid_flag == 0)) {  // 
index f33b5c33c2ed5b7c032bd49ddd86868b772a9012..baf145321edf5e56236c4ba8749e2d3518d909f4 100644 (file)
@@ -741,11 +741,17 @@ static void full_tx_buffer(TRANSMITTER *tx) {
   long isample;
   long qsample;
   double gain, sidevol, ramp, fgain;
+  double *dp;
   int j;
   int error;
-  int mode;
+  int cwmode;
   int sidetone=0;
 
+  // It is important query tx->mode and tune only once, to assure that
+  // the two "if (cwmode)" queries give the same result.
+
+  cwmode = (tx->mode == modeCWL || tx->mode == modeCWU) && !tune;
+
   switch(protocol) {
     case ORIGINAL_PROTOCOL:
       gain=32767.0;  // 16 bit
@@ -755,16 +761,30 @@ static void full_tx_buffer(TRANSMITTER *tx) {
       break;
   }
 
-  mode=vfo[VFO_A].mode;
-  if(split) {
-    mode=vfo[VFO_B].mode;
-  }
-
-  update_vox(tx);
+  if (cwmode) {
+    //
+    // do not update VOX in CW mode in case we have just switched to CW
+    // and tx->mic_input_buffer is non-empty. WDSP (fexchange0) is not
+    // needed because we directly produce the I/Q samples (see below).
+    // What we do, however, is to create the iq_output_buffer for the
+    // sole purpose to display the spectrum of our CW signal. Then,
+    // the difference between poorly-shaped and well-shaped CW pulses
+    // also becomes visible on *our* TX spectrum display.
+    //
+    dp=tx->iq_output_buffer;
+    // These are the I/Q samples that describe our CW signal
+    // The only use we make of it is displaying the spectrum.
+    for (j = 0; j < tx->output_samples; j++) {
+      *dp++ = cw_shape_buffer[j];
+      *dp++ = 0.0;
+    }
+  } else {
+    update_vox(tx);
 
-  fexchange0(tx->id, tx->mic_input_buffer, tx->iq_output_buffer, &error);
-  if(error!=0) {
-    fprintf(stderr,"full_tx_buffer: id=%d fexchange0: error=%d\n",tx->id,error);
+    fexchange0(tx->id, tx->mic_input_buffer, tx->iq_output_buffer, &error);
+    if(error!=0) {
+      fprintf(stderr,"full_tx_buffer: id=%d fexchange0: error=%d\n",tx->id,error);
+    }
   }
 
 #ifdef PURESIGNAL
@@ -778,6 +798,12 @@ static void full_tx_buffer(TRANSMITTER *tx) {
   if(isTransmitting()) {
 
     if(radio->device==NEW_DEVICE_ATLAS && atlas_penelope) {
+      //
+      // On these boards, drive level changes are performed by
+      // scaling the TX IQ samples. In the other cases, DriveLevel
+      // as sent in the C&C frames becomes effective and the IQ
+      // samples are sent with full amplitude.
+      //
       if(tune && !transmitter->tune_use_drive) {
         gain=gain*((double)transmitter->drive_level*100.0/(double)transmitter->tune_percent);
       } else {
@@ -785,35 +811,42 @@ static void full_tx_buffer(TRANSMITTER *tx) {
       }
     }
 
-//  Two times essentially the same code: moved the check on "pulse shape" case
-//  out of the loops for computational efficiency
+//
+//  When doing CW, we do not need WDSP since I(t) = cw_shape_buffer(t) and Q(t)=0
+//  For the old protocol where the IQ and audio samples are tied together, we can
+//  easily generate a synchronous side tone (and use the function
+//  old_protocol_iq_samples_with_sidetone for this purpose).
+//
 
-    if ((mode == modeCWL || mode == modeCWU) && !tune) {
+    if (cwmode) {
        //
        // "pulse shape case":
-       // shape the I/Q samples with the envelope stored in cw_shape_buffer.
-       // We also produce a side tone with same shape.
+       // directly produce the I/Q samples. For a continuous zero-frequency
+       // carrier (as needed for CW) I(t)=1 and Q(t)=0 everywhere. We shape I(t)
+       // with the pulse envelope. We also produce a side tone with same shape.
+       // Note that tx->iq_output_buffer is not used. Therefore, all the
+        // SetTXAPostGen functions are not needed for CW!
        //
-       fgain=gain;                                 // will be multiplied with ramp function
-        sidevol= 258.0 * cw_keyer_sidetone_volume;  // will be multiplied with ramp function
+        sidevol= 258.0 * cw_keyer_sidetone_volume;  // between 0.0 and 32766.0
+       qsample=0;                                  // will be constantly zero
         for(j=0;j<tx->output_samples;j++) {
-           ramp=cw_shape_buffer[j];                // between 0 and 1
-           gain=fgain*ramp;
-           double is=tx->iq_output_buffer[j*2];
-           double qs=tx->iq_output_buffer[(j*2)+1];
-           isample=is>=0.0?(long)floor(is*gain+0.5):(long)ceil(is*gain-0.5);
-           qsample=qs>=0.0?(long)floor(qs*gain+0.5):(long)ceil(qs*gain-0.5);
+           ramp=cw_shape_buffer[j];                // between 0.0 and 1.0
+           isample=floor(gain*ramp+0.5);           // always non-negative, isample is just the pulse envelope
            switch(protocol) {
                case ORIGINAL_PROTOCOL:
+                   //
                    // produce a side tone for internal CW on the HPSDR board
                    // since we may use getNextSidetoneSample for local audio, we need
                    // an independent instance thereof here. To be nice to the CW
                    // operator, the audio is shaped the same way as the RF
+                   // The extra CPU to calculate the side tone is available here,
+                   // because we have not called fexchange0 for this buffer.
+                   //
                    sidetone=sidevol * ramp * getNextInternalSideToneSample();
                    old_protocol_iq_samples_with_sidetone(isample,qsample,sidetone);
                    break;
                case NEW_PROTOCOL:
-                   // ToDo: how to produce side-tone on the HPSDR board?
+                   // ToDo: how to produce synchronous side-tone on the HPSDR board?
                    new_protocol_iq_samples(isample,qsample);
                    break;
            }
@@ -841,17 +874,11 @@ static void full_tx_buffer(TRANSMITTER *tx) {
 }
 
 void add_mic_sample(TRANSMITTER *tx,short mic_sample) {
-  int mode;
+  int mode=tx->mode;
   double sample;
   double mic_sample_double, ramp;
   int i,s;
 
-  if(split) {
-    mode=vfo[1].mode;
-  } else {
-    mode=vfo[0].mode;
-  }
-
 //
 // silence TX audio if not transmitting, if tuning, or
 // when doing CW. Note: CW side tone added later on by a
@@ -1033,9 +1060,9 @@ void tx_set_ps_sample_rate(TRANSMITTER *tx,int rate) {
 // This is the old key-down/key-up interface for iambic.c
 // but now it also smoothes (cw_shape) the signal
 //
-void cw_sidetone_mute(int mute) {
-  if (mute) {
-    cw_key_down = 480000;    // up to 10 sec, should be OK even for QRSS
+void cw_hold_key(int state) {
+  if (state) {
+    cw_key_down = 4800000;    // up to 100 sec
   } else {
     cw_key_down = 0;
   }
index 0f847f501ea1461abd4352babe1e66a779b94568..3b0fb613deccfeb1cc90afcc69541c1f40dca84d 100644 (file)
@@ -145,7 +145,7 @@ extern void transmitter_set_compressor(TRANSMITTER *tx,int state);
 extern void tx_set_ps_sample_rate(TRANSMITTER *tx,int rate);
 extern void add_ps_iq_samples(TRANSMITTER *tx, double i_sample_0,double q_sample_0, double i_sample_1, double q_sample_1);
 
-extern void cw_sidetone_mute(int mute);
+extern void cw_hold_key(int state);
 #endif