Announcement

Collapse
No announcement yet.

Electronic-Nulled Loops

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Electronic-Nulled Loops

    IB loops are typically factory nulled to -60dB or so; that is, VRX = 1mV per every 1V VTX drive. Usually this is done in two steps: coarsely using coil positioning, and finely using a single "fine-tune" loose turn of the RX coil.

    It is not uncommon for loops to drift over time. With epoxy-potted loops, the epoxy can continue to cure for days or weeks after initial potting and cause movement of the coils. Coils placed in and out of severe environments (like the trunk of a car) can also cause drift. I have seen coil drift so bad that the coil null alone causes the detector to overload.

    At White's I researched a number of ways to implement electronic nulling. My preferred goal was to do it all in the loop itself; the loop would have a TX coil, RX coil, bucking coil (maybe), the RX preamp, and a micro. The micro would quadrature-sample the RX signal and adjust a feedforward signal from the TX. (I'm trying to recall this as I type; I don't have my old notes, and I haven't thought about this in 10 years or more.) One idea is to do this only on command, either from a calibration box in production, or from the detector itself. That is, the coil would be nulled, the setting saved in non-volatile memory, and not adjusted again unless needed. I'll call this static e-nulling.

    The other is to adjust the null on a continuous basis, even during use. The attraction of this is that the signal from ground mineralization (which has a phase close to that of a mis-nulled coil) can also be nulled, which could dramatically improve headroom. Let's call this motion e-nulling. The algorithm for implementing motion e-nulling will look a whole lot like something you would do for automatic ground tracking, and would require a target inhibit function.

    As a starting point for discussion, here are two patents for e-nulling. US9989663.pdf​ is from John Earle at White's and uses 2 RX coils, one slightly undernulled and one slightly overnulled. A digital pot is then adjusted to select the best null. One thing I don't like about this approach is that it requires 2 complete RX windings, and depending on the inductance this can add some weight to the coil. Another issue is the bucking coil is wound onto one of the coils but not the other. This can create an R-null difference that is difficult to deal with. Another patent is US5729143.pdf from Zircon. They use PWM signals to create a correction signal with the proper magnitude & phase. This is a little closer to what I was working on, in that correction can be done without adding new coil windings. But I recall I was doing something with the present TX/RX signals, not creating a new signal. I'll have to re-think it out.

    So the challenge here is this: think of ways to implement e-nulling. Remember that you want a good null before the signal even enters the RX preamp so you can get a decent first stage gain without risk of overload. Also think about how a multifrequency coil might be e-nulled.

  • #2
    This circuit is manually adjusted, but the goal is to replace the pots with digipots. The most common digipots are 3-5v which limit the signal that can be processed. There are hv pots which can probably overcome any problems.

    The tx signal is fed into a voltage follower and an inverting amp. The pots are attached to the outputs. One pot controls the in phase through a resistor. The second pot controls the quadrature phase through a capacitor. I have a working test circuit, but I am away until the end January and can provide more detail now.




    Attached Files

    Comment


    • #3
      Click image for larger version  Name:	phase10.gif Views:	0 Size:	1.9 KB ID:	417977
      This one is working very good, pot = 100k, R= 10k, C = 1nF for 8kHz, on the front I added a potentiometer for amplitude regulation.

      Comment


      • #4
        Another method is arado120 detector.
        A variable capacitor placed between tx and rx.
        It was zeroing the coil against slips and deviations.

        https://www.geotech1.com/forums/file...=366974.​​

        Comment


        • #5
          What would be the possible range of values ​​for that R ?
          Digipot is one solution, with some limitations.
          The second thing that comes to mind is the mosfet in the role of variable resistance.
          But it all depends on the possible value span for R.
          The third (many won't like) is a series of resistors that are added/subtracted via digital switches.
          I personally prefer a series of small transistors that simply turn R on/off in series.
          ...
          Have any of you ever paid attention to the series of Russian DIY detectors such as Quant, Fortuna, Soha...
          I did and I made Quant and Fortuna (or was it Soha, I can't remember).
          I don't have the source code, only HEX, so I can't say what was done there.
          But both have a procedure of "adaptation" to any coil, which reminds a lot of what Carl wrote.
          The procedure/function is by calling from the service menu.
          It starts at ~1kHz and goes up to 20kHz and measures the voltage on the RX coil. And finds the most suitable frequency for the given coil.
          I really liked it and tested it extensively on multiple coils. It works almost flawlessly.
          It always finds the best frequency for a given coil. And the detector works great afterwards.

          Just the best solution for a multi-f detector.
          Only, in that case, the "adaptation" process would have to happen very quickly and synchronously with the TX waveform.
          Which is not a problem lately with the flood of very powerful processors and ADC circuits with affordable prices (affordable for professionals and not for amateurs).
          ​​​

          Comment


          • #6
            Originally posted by pito View Post
            Click image for larger version Name:	phase10.gif Views:	0 Size:	1.9 KB ID:	417977
            This one is working very good, pot = 100k, R= 10k, C = 1nF for 8kHz, on the front I added a potentiometer for amplitude regulation.
            Hi all,

            google for "gyrator circuit"... there you will find more information on this circuit and what you can do with it...

            Comment


            • #7
              What I posted can be automated. The Nautilus 2b, Tyndall Electronics used two digipots to null the coil. No micro was involved. It used cmos up/down counters and a halfwave rectifier to sense the rx voltage. I had a 2b and started reversing the nulling pcb, it was a rats nest. I ended up selling the unit before making a schematic. The Nautilus did not use a opamp inverter. The tx oscillator was configured as a Hartley using a center tapped coil. The nulling pots were placed across the tx leads giving 180 degrees of adjustment Most of the posted schematics are incorrect in this section.


              Another interesting coil setup is the Minelab Gofind.

              Comment


              • #8
                Originally posted by Altra View Post
                What I posted can be automated. The Nautilus 2b, Tyndall Electronics used two digipots to null the coil. No micro was involved. It used cmos up/down counters and a halfwave rectifier to sense the rx voltage. I had a 2b and started reversing the nulling pcb, it was a rats nest. I ended up selling the unit before making a schematic. The Nautilus did not use a opamp inverter. The tx oscillator was configured as a Hartley using a center tapped coil. The nulling pots were placed across the tx leads giving 180 degrees of adjustment Most of the posted schematics are incorrect in this section.


                Another interesting coil setup is the Minelab Gofind.
                Pity you didn't trace it out!

                Comment


                • #9

                  Patent #1 = Supper D

                  Click image for larger version  Name:	image.png Views:	0 Size:	1,013.7 KB ID:	418004Click image for larger version  Name:	image.png Views:	0 Size:	62.4 KB ID:	418001Click image for larger version  Name:	image.png Views:	0 Size:	1.04 MB ID:	418002Click image for larger version  Name:	tx4b.jpg Views:	0 Size:	28.3 KB ID:	418003Click image for larger version  Name:	image.png Views:	0 Size:	1.04 MB ID:	418005Click image for larger version

Name:	image.png
Views:	431
Size:	413.9 KB
ID:	418007


                  In my case ground cancelation 90%
                  Attached Files

                  Comment


                  • #10
                    Patent #2 = Needs program modification




                    // This code demonstrates how to generate two output signals
                    // with variable phase shift between them using an AVR Timer

                    // The output shows up on Arduino pin 9 and 10 (these are connected to the Timer1 A and B outputs respectively)

                    // More AVR Timer Tricks at http://josh.com

                    void setup() {

                    pinMode( 9 , OUTPUT ); // Arduino Pin 9 = OCR1A
                    pinMode( 10 , OUTPUT ); // Arduino Pin 10 = OCR1B

                    }


                    // prescaler of 1 will get us 4MHz - 488Hz
                    // User a higher prescaler for lower frequencies

                    #define PRESCALER 1
                    #define PRESCALER_BITS 0x01

                    // Output phase shifted waveforms on Arduino Pins 9 & 10
                    // half_period = 1/2 the period of both waveform in clock ticks (16Mhz clock default) note that the larger this number the lower the freq annd the more phase shift resolution you get
                    // shift = number of ticks delay between leading waveform and trailing. Must be < half_period
                    // invert_b- should we initially invert B? 1=A leads B, 0=B leads A
                    // To be completely out of phase, set shift=0 and invert_b=1

                    void startWaveforms( uint16_t half_period , uint16_t shift , byte invert_b ) {

                    TCCR1B = 0; // Turn off counter if it is on. Makes sure nothing happens while we are getting things set up.
                    // Leaves us in Normal mode, where we can do a Force Compare Match.

                    // First we need to get the phases of the two outputs set up correctly

                    // Set regs so when we force a compare it will match. This is the only way to set the state of the outputs on this chip.

                    //-------------------------------
                    // OCR2A = 127; //50% duty cycle
                    //OCR2B = 64; //about 25% duty cycle
                    OCR1A = 110;
                    OCR1B = 0;
                    TCNT1 = 0;


                    if (invert_b) {
                    TCCR1A = _BV( COM1A1 ) | _BV( COM1B1 ) | _BV( COM1B0 ); // Output A=0 and B=1 on compare match (which we will force presently)
                    } else {
                    TCCR1A = _BV( COM1A1 ) | _BV( COM1B1 ); // Output A=0 and B=0 on compare match (which we will force presently)
                    }

                    // Force a compare.
                    // If polarity=0, then make A=1, B=1
                    // If polarity=1, then make A=1, B=0
                    TCCR1C = _BV( FOC1A ) | _BV( FOC1B );

                    // Now that polarity is established, we can set things ready to run

                    // Both outputs into toggle mode. The output will toggle each time there is a compare match.
                    // Since they both toggle once per period, they will stay in whatever polarity they start in.
                    TCCR1A = _BV( COM1A0 ) | _BV( COM1B0 );

                    // OCR1A = 0;
                    OCR1A = 110;
                    OCR1B = shift;

                    // Set the counter to roll over on the first step. This will force a match when it rolls to 0.
                    TCNT1 = 0xffff;

                    // In CTC Waveform Generation Mode
                    // TCNT counts up to ICR1 then resets back to 0

                    ICR1 = half_period - 1; // We subtract 1 becuase when we get to ICR1 then on the next tick we go back to 0, so will make `period` ticks per cycle total.

                    // Turn on timer now
                    // Mode=CTC, clock=clkio/1 (no prescaling)
                    // Note: you could use a prescaller here for lower frequencies

                    TCCR1B = _BV( WGM13) | _BV( WGM12) | _BV( CS10 );

                    }

                    // Stop output, make both outputs low.

                    void stopWaveforms() {

                    TCCR1A = _BV( COM1A1 ) | _BV( COM1B1 ) ; // Output A=0 and B=0 on next compare match

                    }

                    // Min frequency is 16mhz / 65536 ticks per timer cyclke /2 timer cycles per waveform cycle ~= 122 hz

                    // freq_hz is frequency in hertz from 122 to 4,000,000
                    // shift_deg is phase shift between output A and output B in degrees (values outside -180 to +180 are normalized)

                    // Note that at frequencies above 44KHz you will loose precision on phase shift. At 4Mhz, everything is rounded to just 0 or +/-90 degrees.
                    // Note that all timing happens in 1/16Mhz increments, so frequencies that do not land on integer multipules of that will be aproximiate.
                    // Use startWaveforms() directly for more precise control.

                    void startQuadrature( unsigned long freq_hz , int shift_deg ) {

                    // This assumes prescaler = 1. For lower freqnecies, use a larger prescaler.

                    unsigned long clocks_per_toggle = (F_CPU / freq_hz) / 2; // /2 becuase it takes 2 toggles to make a full wave

                    // Normalize into 0-360 degrees

                    while ( shift_deg < 0 ) {
                    shift_deg += 360;
                    }

                    while ( shift_deg >= 360 ) {
                    shift_deg -= 360;
                    }

                    // Normalize a shift of more than 180 degress

                    byte invert = 0 ;

                    if (shift_deg >= 180 ) {

                    invert = 1 ; // Invert direction
                    shift_deg -= 180;

                    }

                    unsigned long offset_clocks;

                    offset_clocks = (clocks_per_toggle * shift_deg) / 180UL; // Do multiplication first to save precision

                    startWaveforms( clocks_per_toggle , offset_clocks , invert );

                    }

                    void stopQuadrature() {
                    stopWaveforms();
                    }



                    void loop() {

                    // 10KHz, A and B exactly out of phase
                    startQuadrature( 10000 , 180 ); //(-180 same result)
                    delay(10);
                    // stopQuadrature();
                    //delay(1000);

                    }

                    Comment


                    • #11
                      Use the CODE tag for code ("#" button in the editor bar):

                      Code:
                      void setup() {
                        pinMode( 9 , OUTPUT );    // Arduino Pin 9  = OCR1A
                        pinMode( 10 , OUTPUT );   // Arduino Pin 10 = OCR1B
                      }
                      ​

                      Comment


                      • #12
                        Originally posted by ivconic View Post

                        Pity you didn't trace it out!
                        Yes too bad

                        Someone else on the forum has access to one. They posted about a month ago.
                        search Nautilus DMC IIB

                        Comment


                        • #13
                          Originally posted by Carl-NC View Post
                          Use the CODE tag for code ("#" button in the editor bar):

                          Code:
                          void setup() {
                          pinMode( 9 , OUTPUT ); // Arduino Pin 9 = OCR1A
                          pinMode( 10 , OUTPUT ); // Arduino Pin 10 = OCR1B
                          }
                          ​
                          Patent #2 = Needs program modification




                          HTML Code:
                          // This code demonstrates how to generate two output signals
                          // with variable phase shift between them using an AVR Timer
                          
                          // The output shows up on Arduino pin 9 and 10 (these are connected to the Timer1 A and B outputs respectively)
                          
                          // More AVR Timer Tricks at http://josh.com
                          
                          void setup() {
                          
                          pinMode( 9 , OUTPUT ); // Arduino Pin 9 = OCR1A
                          pinMode( 10 , OUTPUT ); // Arduino Pin 10 = OCR1B
                          
                          }
                          
                          
                          // prescaler of 1 will get us 4MHz - 488Hz
                          // User a higher prescaler for lower frequencies
                          
                          #define PRESCALER 1
                          #define PRESCALER_BITS 0x01
                          
                          // Output phase shifted waveforms on Arduino Pins 9 & 10
                          // half_period = 1/2 the period of both waveform in clock ticks (16Mhz clock default) note that the larger this number the lower the freq annd the more phase shift resolution you get
                          // shift = number of ticks delay between leading waveform and trailing. Must be < half_period
                          // invert_b- should we initially invert B? 1=A leads B, 0=B leads A
                          // To be completely out of phase, set shift=0 and invert_b=1
                          
                          void startWaveforms( uint16_t half_period , uint16_t shift , byte invert_b ) {
                          
                          TCCR1B = 0; // Turn off counter if it is on. Makes sure nothing happens while we are getting things set up.
                          // Leaves us in Normal mode, where we can do a Force Compare Match.
                          
                          // First we need to get the phases of the two outputs set up correctly
                          
                          // Set regs so when we force a compare it will match. This is the only way to set the state of the outputs on this chip.
                          
                          //-------------------------------
                          // OCR2A = 127; //50% duty cycle
                          //OCR2B = 64; //about 25% duty cycle
                          OCR1A = 110;
                          OCR1B = 0;
                          TCNT1 = 0;
                          
                          
                          if (invert_b) {
                          TCCR1A = _BV( COM1A1 ) | _BV( COM1B1 ) | _BV( COM1B0 ); // Output A=0 and B=1 on compare match (which we will force presently)
                          } else {
                          TCCR1A = _BV( COM1A1 ) | _BV( COM1B1 ); // Output A=0 and B=0 on compare match (which we will force presently)
                          }
                          
                          // Force a compare.
                          // If polarity=0, then make A=1, B=1
                          // If polarity=1, then make A=1, B=0
                          TCCR1C = _BV( FOC1A ) | _BV( FOC1B );
                          
                          // Now that polarity is established, we can set things ready to run
                          
                          // Both outputs into toggle mode. The output will toggle each time there is a compare match.
                          // Since they both toggle once per period, they will stay in whatever polarity they start in.
                          TCCR1A = _BV( COM1A0 ) | _BV( COM1B0 );
                          
                          // OCR1A = 0;
                          OCR1A = 110;
                          OCR1B = shift;
                          
                          // Set the counter to roll over on the first step. This will force a match when it rolls to 0.
                          TCNT1 = 0xffff;
                          
                          // In CTC Waveform Generation Mode
                          // TCNT counts up to ICR1 then resets back to 0
                          
                          ICR1 = half_period - 1; // We subtract 1 becuase when we get to ICR1 then on the next tick we go back to 0, so will make `period` ticks per cycle total.
                          
                          // Turn on timer now
                          // Mode=CTC, clock=clkio/1 (no prescaling)
                          // Note: you could use a prescaller here for lower frequencies
                          
                          TCCR1B = _BV( WGM13) | _BV( WGM12) | _BV( CS10 );
                          
                          }
                          
                          // Stop output, make both outputs low.
                          
                          void stopWaveforms() {
                          
                          TCCR1A = _BV( COM1A1 ) | _BV( COM1B1 ) ; // Output A=0 and B=0 on next compare match
                          
                          }
                          
                          // Min frequency is 16mhz / 65536 ticks per timer cyclke /2 timer cycles per waveform cycle ~= 122 hz
                          
                          // freq_hz is frequency in hertz from 122 to 4,000,000
                          // shift_deg is phase shift between output A and output B in degrees (values outside -180 to +180 are normalized)
                          
                          // Note that at frequencies above 44KHz you will loose precision on phase shift. At 4Mhz, everything is rounded to just 0 or +/-90 degrees.
                          // Note that all timing happens in 1/16Mhz increments, so frequencies that do not land on integer multipules of that will be aproximiate.
                          // Use startWaveforms() directly for more precise control.
                          
                          void startQuadrature( unsigned long freq_hz , int shift_deg ) {
                          
                          // This assumes prescaler = 1. For lower freqnecies, use a larger prescaler.
                          
                          unsigned long clocks_per_toggle = (F_CPU / freq_hz) / 2; // /2 becuase it takes 2 toggles to make a full wave
                          
                          // Normalize into 0-360 degrees
                          
                          while ( shift_deg < 0 ) {
                          shift_deg += 360;
                          }
                          
                          while ( shift_deg >= 360 ) {
                          shift_deg -= 360;
                          }
                          
                          // Normalize a shift of more than 180 degress
                          
                          byte invert = 0 ;
                          
                          if (shift_deg >= 180 ) {
                          
                          invert = 1 ; // Invert direction
                          shift_deg -= 180;
                          
                          }
                          
                          unsigned long offset_clocks;
                          
                          offset_clocks = (clocks_per_toggle * shift_deg) / 180UL; // Do multiplication first to save precision
                          
                          startWaveforms( clocks_per_toggle , offset_clocks , invert );
                          
                          }
                          
                          void stopQuadrature() {
                          stopWaveforms();
                          }
                          
                          
                          
                          void loop() {
                          
                          // 10KHz, A and B exactly out of phase
                          startQuadrature( 10000 , 180 ); //(-180 same result)
                          delay(10);
                          // stopQuadrature();
                          //delay(1000);
                          
                          }​

                          Comment


                          • #14
                            Originally posted by Altra View Post

                            Yes too bad

                            Someone else on the forum has access to one. They posted about a month ago.
                            search Nautilus DMC IIB

                            Comment


                            • #15
                              I think I might be able to draw a layout of the auto null board based on the photos.
                              A schematic can then be drawn. It should be easy to discern what chips were used.
                              The component values aren't important. Just to grasp how the circuit is doing it's job.

                              Comment

                              Working...