C-field driver - again

After some work I have designed at this C-field driver:

../../../_images/A15_cfld.svg

The main change since first version a couple of extra resistors supply the stable current for the buried zener in the LM399 voltage reference.

I’ve run a monte-carlo analysis for the tempco of the components over a ±10 {K} temperature range.

VCC & R4

VCC is the +20V regulated supply, which I allowed to vary ±0.5 {V}

If R4 is 1M with 100 {ppm/K} tempco, the frequency noise is 6.5e-17 {s/s}.

If R4 is 100k with 100 {ppm/K} tempco, the frequency noise is 5.7e-16 {s/s}.

I have not checked how small R4 needs to be to ensure startup, but given the low offset voltage of U1, I don’t expect problems.

R2 & R3

The voltage over these is the same (± U1’s Vos) and their ratio sets the U2 zener current relative to the C-field current.

With 50 {ppm/K} each of them contributes 6.7e-17 {s/s} frequency noise.

U1

LT1007 has a guaranteed max drift with temperature of 0.6 {µV/K}, which contributes 1.1e-15 {s/s} frequency noise.

There is no need to trim the offset of U1.

R1 & U2

R1 is a Vishay Z201 with a typical tempco of ±0.2 {ppm/K}

U2 is a LM399 (without -A suffix) with a typical tempco 0.3 {ppm/K}

If we use these typical tempcos, the frequency noise is 4.3e-15 {s/s}

If we use the max tempco, 2 {ppm/K}, for the LM399 and the typical for R1, the noise rises to 2.4e-14 which is above my 1e-14 {s/s} goal.

There is no max tempco specified for the Z201, and the datasheet doesn’t give me high confidence that the typical is very typical after all.

Temperature

The limiting factor is R1 and U2, and all in all there is about 1.5 {ppm/K} tempco to share between them.

Of course ±10 {K} is a pretty brutal condition for a device which presumably lives in a somewhat civilized laboratory.

If we can keep the excursions to ±2{K}, then R1 can have 3 {ppm/K} tempco and still stay below target.

Tunable C-field current

Mechanical trimmers have two digit tempcos, and since they would go between U2 and U1, math isn’t even necessary.

A digital solution may be possible, there are special DACs for driving 4-20 {mA} industrial circuits, for instance Analog’s AD5422, which could possibly be beaten into submission, given a good external reference.

That would be neat, since it would open the way for a GPS discipline implementation, but that is a project for another day.

Right now I just want to get the Fluke 732A and the long wire out of the picture.

Monte-Carlo Code

Here is my monte-carlo simulation in python:

#!/usr/bin/env python
#
#
# VCC
#  |
#  |
# R4
#  |              |\
#  *--------------| \
#  |              |  >--- L1 --+
#  |           +--| /          |
#  |           |  |/           |
#  |           |               |
#  *---- R3 -------+-----------+
#  |           |   |
#  |           |  R2
#  |           |   |
# D1           +---*
#  |               |
#  |              R1
#  |               |
# GND             GND
#

from __future__ import print_function

import random
import math

def calc(comp):
    d = dict(comp)
    for i in range(5):
            d["vr1"] = d["D1"] + d["U1"]
            d["ir1"] = d["vr1"] / d["R1"]
            d["vr2"] = d["ir1"] * d["R2"]
            d["vl1"] = d["ir1"] * d["L1"]
            d["vout"] = d["vl1"] + d["vr2"] + d["vr1"]
            d["vr3"] = d["vr1"] + d["vr2"] - d["D1"]
            d["ir3"] = d["vr3"] / d["R3"]
            d["ir4"] = (d["VCC"] - d["D1"]) / d["R4"]
            d["id1"] = d["ir3"] + d["ir4"]
            d["D1"] = comp["D1"] + d["id1"] * .66
    return d

def show(d):
    l = d.keys()
    l.sort()
    for i in l:
            print("%s\t%17.9f" % (i, d[i]))

def ppm(n):
    r = random.uniform(-n, n)
    r *= 1e-6
    r += 1.0
    return r

comp0 = {
    "R1":   5000./3,        # Ohm
    #"R1":  1000.,          # Ohm
    "R2":    500,           # Ohm
    "R3":   2000,           # Ohm
    "R4":    1e5,           # Ohm
    "D1":      6.9,         # Volt
    "L1":     12.0,         # Ohm
    "U1":      0.0,         # Volt
    "VCC":    20.0,         # Volt
}

d = calc(comp0)
show(d)
iref = d["ir1"]

sy = 0.0
syy = 0.0
n = 0.0
for i in range(1000):
    x = dict(comp0)
    dt = 10

    # 2.4e-15 @ 2ppm (LM399), +/- 10K
    x["D1"] *= ppm(2 * dt)

    # 2.5e-15 @ .2ppm (Z201 TYP!), +/- 10K
    x["R1"] *= ppm(3.0 * dt)

    # 1.1e-15 @ .6PPM/K (LT1007A), +/- 10K
    x["U1"] = 1e-6 * random.uniform(-.6 * dt, .6 * dt)

    # 6.7e-17 @ 50pmm, +/- 10K
    x["R3"] *= ppm(50 * dt)

    # 6.7e-17 @ 50ppm, +/- 10K
    x["R2"] *= ppm(50 * dt)

    # 6.5e-17 @ 100ppm, +/- 10K, 19.5-20.5 VCC
    x["VCC"] = random.uniform(19.5, 20.5)
    x["R4"] *= ppm(100 * dt)

    d = calc(x)
    df = (d["ir1"] - iref) * 5e-7
    sy += df
    syy += df * df
    n += 1.0

avg = sy / n
stddev = math.sqrt((syy - sy * sy / n) / (n - 1))
print("%.1e" % stddev)

phk