Return to KLUBNL.PL main page

rsgb_lf_group
[Top] [All Lists]

Re: LF: JT9-5 decodes in km07ks

To: [email protected]
Subject: Re: LF: JT9-5 decodes in km07ks
From: DK7FC <[email protected]>
Date: Sun, 30 Dec 2018 17:22:54 +0100
Dkim-signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.de; s=2017; t=1546186975; bh=SiRQIfLqHnxV2uj+9uULRhlUe7GfNUaQesjrul1njrI=; h=Date:From:To:Subject:From; b=IQd/CdcrQhGfqSlcJ6qGWlMgfpUnaWIppnpuDifobKuu9+qpeJTW2nETqnUYgqQLK s/mHjAFVjCOzxWuKuTxbugjrGck+cmthigN7h4khuMCIaeS6icgtoU6vN7MkeEzfaK GkvGCrdeR31fEmpAzsB5J4xngw+QsJmRrKwzBYwluZ3rKelNfMXU54kJg3JqPFGAjM iJnVNEm914dNN3nlm8+AIMYJRFpaYdkvW6LkMTZHRSWwQ4WUgiABnjHAVxnzu/a65y V6OXZNlvDxaocPBawn6H+GiyctouYukRRuBNT+GatEWkFoq6QO4pNm240Hao8cXmwZ To1QjrKddyhhA==
In-reply-to: <[email protected]>
References: <[email protected]> <[email protected]> <[email protected]> <[email protected]>
Reply-to: [email protected]
Sender: [email protected]
User-agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.1.8) Gecko/20100227 Thunderbird/3.0.3
OMG, did someone say that EbNaut is difficult??
Orders of magnitude of complexity seem to be out there..

73, Stefan

Am 29.12.2018 21:54, schrieb Wolfgang Büscher:

Below: Core of the "universal frequency converter / mixer" as currently used in Spectrum Lab (this is the simple one, without forward and reverse FFT).


void SOUND_RunThroughMixer(
      T_SOUND_FREQ_CONVERTER *pcnv,  // contains all parameters for a converter
      T_Float *input_samples,  T_Float *input_samples_q,
      T_Float *output_samples, T_Float *output_samples_q,
      int    number_of_samples )
 /* Frequency mixer. May transform the frequency for some kind of RX.
  * Can optionally cancel one sideband using a Hilbert transformer,
  * I/Q-mixer etc.
  */
{
 int    sample_i, j;
 T_Float d, y, dbl_i, dbl_q;
 const T_Float* coeff_ptr;
 T_Float* queue_ptr;
 T_Float* input_queue_end;
 double  dblPhzInc;
 int     iCosTableIndex, iSineTableOffset = 0;
 T_Float dblNcoI, dblNcoQ;
 double  dblNcoPhase = pcnv->dblNcoPhase;  // use local var for speed


  // Prepare the NCO for the "mixer" frequency ...
  // Note: SoundTab_fltCosTable[SOUND_COS_TABLE_LEN] contains exactly ONE PERIOD.
  //       "Negative" frequencies are possible by inverting the Q-channel .
  if(pcnv->dblFSample!=0)
      dblPhzInc = (double)SOUND_COS_TABLE_LEN * pcnv->dblOscillatorFreq / pcnv->dblFSample;
    else
      dblPhzInc = 0;   // mixer frequency is ZERO, so noo phase increment

  if(dblPhzInc > 0)
   { // array index offset to read a SINE WAVE from a COSINE TABLE :
     iSineTableOffset = (int) (0.499 + 3.0 * (T_Float)SOUND_COS_TABLE_LEN / 4.0);
   }
  else   // phase increment negative, "negative" mixer frequency ....
   {
     dblPhzInc = -dblPhzInc; // Make sure the table reading index INCREMENTS,
                             // because the wrap test will not look for negative indices.
     // Get array index offset to read a NEGATIVE SINE WAVE from a COSINE TABLE :
     iSineTableOffset = (int)(0.499 + (T_Float)SOUND_COS_TABLE_LEN / 4.0);
   } // end if <produce NEGATIVE frequencies ... possible only with I/Q mult. >
  if(dblNcoPhase<0) dblNcoPhase = 0;


  if(pcnv->fDcReject)
   { // Accumulate & remove a bit DC offset from the new chunk:
     for(sample_i=0; sample_i<number_of_samples; ++sample_i)
       {
         pcnv->dblMixerDcOffset =
                     pcnv->dblMixerDcOffset  * 0.9999
                   + input_samples[sample_i] * 0.0001;
         // bad style to modifying the INPUT samples
         //  but this won't hurt anyone :>
         input_samples[sample_i] -= pcnv->dblMixerDcOffset;
       }
   }

  if(pcnv->iMixerScheme==CFG_FREQ_MIX_DSB)
   { // No sideband rejection:
     //   This is a simple and very fast mixer without sideband-rejection.
     //   No complicated broadband 90° phase shifter required.
    for(sample_i=0; sample_i<number_of_samples; ++sample_i)
     {
      // Run the NCO (numerical controlled oscillator)
      dblNcoPhase += dblPhzInc;
      while(dblNcoPhase >=(T_Float)SOUND_COS_TABLE_LEN) // "while", not "if" !!
            dblNcoPhase -=(T_Float)SOUND_COS_TABLE_LEN; // table index wrap
      d = SoundTab_fltCosTable[(int)dblNcoPhase];

      // DSB mixer: simply multiply the input signal with the NCO output :
      *output_samples++ = (*input_samples++) * d;
     }
   }  // end if <frequency converter WITHOUT sideband rejection>
  else
  if(pcnv->iMixerScheme==CFG_FREQ_MIX_COMPLEX)
   { // Complex multiplication of complex input :
     //   (a + j*b ) * ( c + j*d )  =  a*c - b*d + j * ( a*d + b*c )
     // To do this, SOUND_RunThroughMixer() must be called with
     //   TWO source-  and TWO destination blocks.
     //  (real and imaginary parts are in sepearate blocks)
     if(  (input_samples!=NULL) && (input_samples_q!=NULL)
        &&(output_samples!=NULL) && (output_samples_q!=NULL) )
      {
        T_Float inp_i, inp_q, outp_i, outp_q;
        for(sample_i=0; sample_i<number_of_samples; ++sample_i)
         {
          // Run the NCO (numerical controlled oscillator; here with quadrature output)
          dblNcoPhase += dblPhzInc;
          while(dblNcoPhase >=(T_Float)SOUND_COS_TABLE_LEN) // "while", not "if" !!
                dblNcoPhase -=(T_Float)SOUND_COS_TABLE_LEN; // table index wrap
          iCosTableIndex = (int)dblNcoPhase;
          dblNcoI = SoundTab_fltCosTable[iCosTableIndex];
          dblNcoQ = SoundTab_fltCosTable[(iCosTableIndex+iSineTableOffset) % SOUND_COS_TABLE_LEN];

          // Get complex input sample, multiply with complex oscillator, and put into output buffers:
          inp_i = *(input_samples++);
          inp_q = *(input_samples_q++);
          outp_i = inp_i * dblNcoI - inp_q * dblNcoQ;
          outp_q = inp_i * dblNcoQ + inp_q * dblNcoI;
          *output_samples++   = outp_i;
          *output_samples_q++ = outp_q;
         }
     }
   }  // end if <frequency converter WITHOUT sideband rejection>
  else
  if(   (pcnv->iMixerScheme == CFG_FREQ_MIX_LSB_DOWNCONVERTER )
     || (pcnv->iMixerScheme == CFG_FREQ_MIX_USB_DOWNCONVERTER ) )
   {   // Optimized mixer for "downconversion".
       // Based on a schematic diagram from the ARRL handbook 1996,
       // page 17.73:   "The R2: An image-rejecting D-C Receiver"
       // Principle: Splitter, TWO Mixers (with I and Q output),
       //            audio phase shift network, summer, filter.
       // Note: 90° phase shifting is done AFTER mixing (in contrast to the
       //           schemes  CFG_FREQ_MIX_LSB_UP  and  CFG_FREQ_MIX_USB_UP.
    for(sample_i=0; sample_i<number_of_samples; ++sample_i)
     {
      // Let the NCO (numerical controlled oscillator) produce quadrature-phase signals :
      dblNcoPhase += dblPhzInc;
      while(dblNcoPhase >=(T_Float)SOUND_COS_TABLE_LEN)  // "while", not "if" !!
            dblNcoPhase -=(T_Float)SOUND_COS_TABLE_LEN; // table index wrap
      iCosTableIndex = (int)dblNcoPhase;
      dblNcoI = SoundTab_fltCosTable[iCosTableIndex];
      dblNcoQ = SoundTab_fltCosTable[(iCosTableIndex+iSineTableOffset) % SOUND_COS_TABLE_LEN];

      // Now mix the incoming (higher-frequency) signal ..input_samples[]..
      //         with a sine and a cosine signal from the NCO ("LO").
      dbl_i = input_samples[sample_i];   // this is the INPUT signal

      if(pcnv->iMixerScheme == CFG_FREQ_MIX_LSB_DOWNCONVERTER)
       {
        dbl_q =  dbl_i * dblNcoI;   // LSB (below NCO freq) moved DOWN
        dbl_i =  dbl_i * dblNcoQ;
       }
      else  // the "other" sideband with reversed oscillator phases:
       {
        dbl_q =  dbl_i * dblNcoQ;   // USB (above NCO freq) moved DOWN
        dbl_i =  dbl_i * dblNcoI;
       }
      // arrived here: dbl_i is the output of the "I"-mixer,
      //               dbl_q is the output of the "Q"-mixer.


      // Let the I-channel run through a delay line [ length (N-1)/2 ]
      //  to compensate the delay from the hilbert trafo (see below).
      if(  (pcnv->iDelayQIndex < 0)
         ||(pcnv->iDelayQIndex >= ((SOUND_Hilbert_filter_length-1)/2)) )
            pcnv->iDelayQIndex = 0;
      y = pcnv->dblDelayQueue[pcnv->iDelayQIndex];
      pcnv->dblDelayQueue[pcnv->iDelayQIndex++] = dbl_i;
      dbl_i = y;

      // Let the Q-channel run through a broadband 90° phase shifter
      // (hilbert transformer, implemented as FIR filter here).
      //    keep the circular buffer pointer 'valid' all the time :
      input_queue_end = &pcnv->dblHilbertQueue[SOUND_Hilbert_filter_length-1];
      if(  (pcnv->pdblHilbertQPointer < &pcnv->dblHilbertQueue[0])
         ||(pcnv->pdblHilbertQPointer > input_queue_end) )
            pcnv->pdblHilbertQPointer = &pcnv->dblHilbertQueue[0];
      if( --pcnv->pdblHilbertQPointer < &pcnv->dblHilbertQueue[0] )
            pcnv->pdblHilbertQPointer = input_queue_end; // deal with wraparound
      *pcnv->pdblHilbertQPointer = dbl_q;     // hilbert trafo input
      queue_ptr = pcnv->pdblHilbertQPointer;  // pointer to read from input queue
      coeff_ptr = &SOUND_HilbertCoeffs[0]; // pointer to read from coeff table
      y = 0.0;                  // clear the 'global adder'
      j = (SOUND_Hilbert_filter_length+1)/2 -1;  // j=12 for 25th-order filter
      while( j-- )              // do the MAC's (with every 2nd coeff ZERO)
        {
         // coeffs[0], [2], [4].. are (almost) zero and are not calculated
         //                       so just skip the 'queue' pointer
         ++queue_ptr;  ++coeff_ptr;
         if( queue_ptr > input_queue_end ) // deal with wraparound
             queue_ptr = &pcnv->dblHilbertQueue[0];
         // odd coefficients, non-zero, do a MAC-operation :
         y += ( (*queue_ptr++) * (*coeff_ptr++) );
         if( queue_ptr > input_queue_end ) // deal with wraparound
             queue_ptr = &pcnv->dblHilbertQueue[0];
        }
      dbl_q = y;    // filter output = sum from all 'taps'

      // finally add the output of the "audio phase-shift network"...
      output_samples[sample_i] = dbl_i + dbl_q;

     } // end for (sample_i ... )
   } // end if <..mixer_scheme == CFG_FREQ_MIX_USB_DOWNCONVERTER >
  else // neither "double side band" nor "USB DOWNCONVERTER"....
   {
    for(sample_i=0; sample_i<number_of_samples; ++sample_i)
     {
      // Let the I-value run through a delay line [ length (N-1)/2 ]
      //  to compensate the delay from the hilbert trafo (see below).
      if(  (pcnv->iDelayQIndex < 0)
         ||(pcnv->iDelayQIndex >= ((SOUND_Hilbert_filter_length-1)/2)) )
            pcnv->iDelayQIndex = 0;
      dbl_i = pcnv->dblDelayQueue[pcnv->iDelayQIndex];
      pcnv->dblDelayQueue[pcnv->iDelayQIndex++]
            = input_samples[sample_i];

      // Generate the Q-value by passing the real input through a
      // hilbert transformer (which is implemented as an FIR filter here).
      //    keep the circular buffer pointer 'valid' all the time :
      input_queue_end = &pcnv->dblHilbertQueue[SOUND_Hilbert_filter_length-1];
      if(  (pcnv->pdblHilbertQPointer < &pcnv->dblHilbertQueue[0])
         ||(pcnv->pdblHilbertQPointer > input_queue_end) )
            pcnv->pdblHilbertQPointer = &pcnv->dblHilbertQueue[0];
      if( --pcnv->pdblHilbertQPointer < &pcnv->dblHilbertQueue[0] )
            pcnv->pdblHilbertQPointer = input_queue_end; // deal with wraparound
      *pcnv->pdblHilbertQPointer = input_samples[sample_i];  // filter input = "X"
      queue_ptr = pcnv->pdblHilbertQPointer;  // pointer to read from input queue
      coeff_ptr = &SOUND_HilbertCoeffs[0]; // pointer to read from coeff table
      y = 0.0;                  // clear the 'global adder'
      j = (SOUND_Hilbert_filter_length+1)/2 -1;  // j=12 for 25th-order filter
      while( j-- )              // do the MAC's (with every 2nd coeff ZERO)
        {
         // coeffs[0], [2], [4].. are (almost) zero and are not calculated
         //                       so just skip the 'queue' pointer
#if(0)   // not optimized:
         y += ( (*queue_ptr++) * (*coeff_ptr++) );  // here's the MAC
#else    // optimized:
         ++queue_ptr;  ++coeff_ptr;
#endif
         if( queue_ptr > input_queue_end ) // deal with wraparound
             queue_ptr = &pcnv->dblHilbertQueue[0];
         // odd coefficients, non-zero, do a MAC-operation :
         y += ( (*queue_ptr++) * (*coeff_ptr++) );
         if( queue_ptr > input_queue_end ) // deal with wraparound
             queue_ptr = &pcnv->dblHilbertQueue[0];
        }
      dbl_q = y;    // filter output = sum from all 'taps'

      // Let the NCO (numerical controlled oscillator) produce quadrature-phase signals :
      dblNcoPhase += dblPhzInc;
      while(dblNcoPhase >=(T_Float)SOUND_COS_TABLE_LEN) // "while", not "if" !!
            dblNcoPhase -=(T_Float)SOUND_COS_TABLE_LEN; // table index wrap
      iCosTableIndex = (int)dblNcoPhase;
      dblNcoI = SoundTab_fltCosTable[iCosTableIndex];
      dblNcoQ = SoundTab_fltCosTable[(iCosTableIndex + iSineTableOffset) % SOUND_COS_TABLE_LEN];

      if(pcnv->iMixerScheme==CFG_FREQ_MIX_LSB_UP)
       {
        // multiply the I- and Q- samples with the sin- and cos- output
        //     of the local oscillator.
        output_samples[sample_i] =
              dbl_q * dblNcoI  // multiply with "cosine"
            - dbl_i * dblNcoQ; // multiply with "sine"
       }
      else // not LSB but USB:
       {
        output_samples[sample_i] =
              dbl_i * dblNcoI  // multiply with "cosine"
            - dbl_q * dblNcoQ; // multiply with "sine"
       }
     } // end for (sample_i ... )
   } // end if <frequency UP converter with sideband rejection>

  pcnv->dblNcoPhase = dblNcoPhase;  // save the NCO phase for next block

} // end SOUND_RunThroughMixer()




On 29.12.2018 10:47, Rik Strobbe wrote:

Hello Markus,


nice copy on N1BUG, this might be a new distance record for Paul.


Thanks for your help with the JT9-2 and JT9-5 resampling.

Lifting the frequency restriction on TX is "peanuts", but on RX I would need to implement frequency conversion in SpecLab style.

I will have a look to that (maybe I should ask Wolf for advice).


73, Rik  ON7YD - OR7T




Van: [email protected] <[email protected]> namens Markus Vester <[email protected]>
Verzonden: zaterdag 29 december 2018 10:28
Aan: [email protected]
Onderwerp: Re: LF: JT9-5 decodes in km07ks
 
Thanks to Rik for the software, and to Spiros and Dionysios for their reports. This is what I picked up using JT9-2

18-12-28 21:38 2038 -25  0.1  648 2  CQ SV8CS KM07        
18-12-28 21:40 2040 -25  0.1 1200 2  LA3EQ JO28XJ         
18-12-28 21:42 2042 -26  0.1  648 2  CQ SV8CS KM07        
18-12-28 21:44 2044 -26  0.2 1200 2  LA3EQ JO28XJ         
18-12-28 21:48 2048 -25  0.1 1250 2  LA3EQ JO28XJ         
18-12-28 21:52 2052 -25  0.2 1250 2  LA3EQ JO28XJ         
18-12-28 21:56 2056 -25  0.2 1250 2  LA3EQ JO28XJ         
18-12-28 22:00 2100 -28  0.3 1250 2  LA3EQ JO28XJ         

and later JT9-5:

18-12-29 02:30 0130 -34 -0.7  615 5  VVV N1BUG 5          
18-12-29 02:40 0140 -34 -0.6  615 5  VVV N1BUG 5          
18-12-29 03:30 0230 -35 -0.6  615 5  VVV N1BUG 5          

As my LF transceiver needs to be parked on 135.5 kHz I am always struggling with audio frequency restrictions in WSJT, WSPR and JT-9 software. My workaround are frequency conversions in two separate SpecLab instances for RX and TX, connected to the left and right channels of a single VB-Audio virtual cable instance. However there is a noticable latency in this process.

Best 73,
Markus (DF6NM)


-----Ursprüngliche Mitteilung-----
Von: Dionysios Vlachiotis <[email protected]>
An: rsgb_lf_group <[email protected]>; rsgb_lf_group <[email protected]>
Verschickt: Sa, 29. Dez 2018 8:39
Betreff: LF: JT9-5 decodes in km07ks

245 -28 -0.4  650 5  CQ DF6NM JN59        
2255 -27 -0.4  650 5  CQ DF6NM JN59        
2315 -27 -0.5  650 5  CQ DF6NM JN59        
2325 -24 -0.3  650 5  CQ DF6NM JN59        
0035 -23 -0.2  650 5  CQ DF6NM JN59        
0045 -21 -0.3  650 5  CQ DF6NM JN59     
 
73 de SV8RV
Zakynthos isl. GREECE (km07ks)

-----Ursprüngliche Mitteilung-----
Von: SV8CS-Spiros Chimarios <[email protected]>
An: rsgb_lf_group <[email protected]>
Verschickt: Sa, 29. Dez 2018 5:56
Betreff: LF: SV8CS SlowJT9

Good morning.
Decoded only DF6NM during the night (JT9-5) – 136 KHz.
 
2245 -30 -0.4  650 5  CQ DF6NM JN59        
2255 -29 -0.4  650 5  CQ DF6NM JN59        
2315 -27 -0.4  650 5  CQ DF6NM JN59        
2325 -26 -0.4  650 5  CQ DF6NM JN59        
0035 -21 -0.6  650 5  CQ DF6NM JN59        
0045 -20 -0.4  650 5  CQ DF6NM JN59 
 
73, Spiros/SV8CS

<Prev in Thread] Current Thread [Next in Thread>