Heterodyne tracking calculator

Introduce

Totally free and open source.

This calculator take standard design parameters, then give a close to betest tracking result by  selecting the almost best 'tracking' frequencies instead of use 500 KHZ, 1000 KHz , 1.500 Mhz.

suitable not only AM band. free to choose signal bands and Intermediate frequency.

(NOTE: the Ct is always assume to be zero)


Usage

python track.py --IF 1000 --Cmax=355 --Cmin=12.7 --Fmin=0.52  --Fmax=1.65

Output: 

The Red line is tracking error align with the variable capacitors

Signal Pad Capacitor CL: 10.12 pF

Signal tank inductance: 7.70827247054 uH

============  ignore Ct==============

Exactly Tracking points:: 3.495 6.87 11.28 Mhz

LO Capacitor Cm: 11.1483876571 pF

LO Pad Capacitor Cp: 1718.55017412 pF

Lo Inductance Lz: 6.96 uH

============Tracking graph===============

Max tracing error:  18.2013733297 KHz

Get help:

python track.py --help

Usage: track.py [options]

Options:

  -h, --help   show this help message and exit

  --Fmax=FMAX  Signal Fmax in MHz

  --Fmin=FMIN  Signal Fmin in MHz

  --IF=IF      IF in KHz

  --Cmin=CMIN  Variable min in pF

  --Cmax=CMAX  Variable max in pF


Install and Running this python based calculator from source code


1. copy and paste following source code to a track.py file

2. install some packages(linux or windows, etc)

    pip install numpy  matplotlib

3. now you can running it from IDE or command line(i prefer command line)

track.py

#!/usr/bin/env python

# -*- coding: utf-8 -*-

"""

  Trancking Caclulator

   python track.py --Fmax 1.65 --Fmin 0.52 --IF 455 --Cmin=12 --Cmax=360

  

  by BH1RBG @May 22 2017

"""

import math

import numpy as np

import sys

import matplotlib.pyplot as plt

from optparse import OptionParser

# constants

pi = math.pi

pF=math.pow(10,-12)

#nF=math.pow(10, -9)

uH=math.pow(10, -6)

MHz=math.pow(10, 6)

KHz=math.pow(10, 3)

"""

   Input args:

   

"""

parser = OptionParser()

parser.add_option("--Fmax", type="float", dest="Fmax",

                  help="Signal Fmax in MHz", metavar="FMAX", default=0.520)

parser.add_option("--Fmin", type="float", dest="Fmin",

                  help="Signal Fmin in MHz", metavar="FMIN", default=1.650)

parser.add_option("--IF", type="float", dest="IF",

                  help="IF in KHz", metavar="IF", default=455)

parser.add_option("--Cmin", type="float", dest="Cmin",

                  help="Variable min in pF", metavar="CMIN", default=12.5)

parser.add_option("--Cmax", type="float", dest="Cmax",

                  help="Variable max in pF", metavar="CMAX", default=350)

options, args = parser.parse_args()

#### Configrations

#Czl=options.Cvl*pF

#Czh=options.Czl*pf

Czl = 12*pF  # My 12.7  pf

Czh = 350*pF # My 344.5 pf

Czl = options.Cmin*pF

Czh = options.Cmax*pF

Crmin = Czl

Crmax = Czh

Fmin = options.Fmin*MHz #510*KHz    #波段最低

Fmax = options.Fmax*MHz #1.650*MHz  #波段最高

IF = options.IF*KHz #455*KHz  # 中频频率

# 追踪点

f1=600*KHz

f2=1000*KHz

f3=1500*KHz

"""" Choose point method A

<<收音机的统调>> 公式6-10

## max diff 7.5khz

"""

"""

f1=Fmin + 0.067*(Fmax-Fmin)

f2=Fmin + 0.5*(Fmax-Fmin)

f3=Fmin + 0.933*(Fmax-Fmin)

"""

"""" Choose point method B

蔡金涛 <<统一调谐>>, 

<<收音机的统调>> 公式 6-11"""

"""

f1=Fmin*1.01 + 0.03*(Fmax-Fmin)

f2=Fmin*1.17 + 0.27*(Fmax-Fmin)

f3=Fmin*1.08 + 0.84*(Fmax-Fmin)

"""

""" <<收音机中的统调》 公式6-10 调优1 """

f1=Fmin + 0.066*(Fmax-Fmin)

f2=Fmin + 0.405*(Fmax-Fmin)

f3=Fmin + 0.9117*(Fmax-Fmin)

## <<收音机中的统调》 公式6-10 调优2

f1=Fmin + 0.055*(Fmax - Fmin)

f2=Fmin + 0.43* (Fmax - Fmin)

f3=Fmin + 0.92*(Fmax - Fmin)

"""

    所有计算基于下图:

                                                 Cp

                                                 ||

                                         .----o--||---o----.

             .-------o-----.             |    |  ||   |    |

             |       |     |             |    |       |    |

             C|      |     | Cr          C|   |       |    |

          Lr C|  CL ---   ---         Lz C|  ---     ---  ---

             C|     ---   ---            C|  ---Ct Cm---  --- Cz

             |       |     |             |    |    ,  |    |

             '-------o--,´-'              ----o-,-´---o----'

                      ,´                     ,-´

                      --------------------  ´

               Signal Input                  Oscllator Tank

 

"""

## Defautl Unit: Mhz, uH, pF

Crmax = Crmax/pF

Crmin = Crmin/pF

f1 = f1/MHz

f2 = f2/MHz

f3 = f3/MHz

IF = IF/MHz

Fmin = Fmin/MHz

Fmax = Fmax/MHz

## 覆盖系数

k = math.sqrt( Fmax*Fmax/(Fmin*Fmin) )

CL = ( Crmax-k*k*Crmin )/( k*k-1 )

print "Signal Pad Capacitor CL:", CL, "pF"

# CL:pF, fmin:Mhz, Lr: uH

Lr = (25330)/( math.pow(Fmin,2)*(Crmax+CL))

print "Signal tank inductance Lr:", Lr, "uH"

### 震荡回路计算

## 三点统调 f1,f2, f3 对应的Cr1, Cr2, Cr3: pf

Cr1 = 25330/(f1*f1*Lr) - CL

Cr2 = 25330/(f2*f2*Lr) - CL

Cr3 = 25330/(f3*f3*Lr) - CL

#print "### Cr1, Cr2 , Cr3: ", Cr1, Cr2, Cr3," pF"

#等容, 所以对应频率震荡电路的三个角度下可变电容容量亦同, pf

Cz1=Cr1

Cz2=Cr2

Cz3=Cr3

# 三点跟踪, 故, f1, f2, f3对应的震荡回路频率是 (MHz)

fz1 = (f1+IF)

fz2 = (f2+IF)

fz3 = (f3+IF)

#print "### fz1, fz2 , fz3: ", fz1, fz2, fz3," MHz"

#fz1,fz2,fz3之间的覆盖系数

kz31 = math.sqrt( (fz3*fz3) / (fz1*fz1) )

kz32 = math.sqrt( (fz3*fz3) / (fz2*fz2) )

#print "### kz31^2, kz32^2 ", kz31*kz31, kz32*kz32

A = ( kz31*kz31-1) / ( kz31*kz31-kz32*kz32)

B = ( Cz1-Cz3 )/( Cz1-Cz2 )

H = ( B*Cz2 - A*Cz3) / (A-B)

#print "### A, B, H", A, B, H 

"""

    忽略Ct:

                                                 Cp

                                                 ||

                                         .-------||---o----.

             .-------o-----.             |       ||   |    |

             |       |     |             |            |    |

             C|      |     | Cr          C|           |    |

          Lr C|  CL ---   ---         Lz C|          ---  ---

             C|     ---   ---            C|       Cm ---  --- Cz

             |       |     |             |        ,  |    |

             '-------o--,´-'              ------,-´---o----'

                      ,´                     ,-´

                      --------------------  ´

               Signal Input                  Oscllator Tank

 

"""

print "============ ignore Ct=============="

print "Exactly Tracking points:", f1, f2, f3, "Mhz"

##忽略Ct(线圈电容)则

h =  (Cz1 + H) / (Cz3 +H)

Cm = ( Cz1 - kz31*kz31*h*Cz3) / ( kz31*kz31*h - 1)

print "LO Capacitor Cm:", Cm, "pF"

Cp = H - Cm

print "LO Pad Capacitor Cp:", Cp, "pF"

C3 = (Cz3+Cm)*Cp / (Cz3+H)

Lz = 25330/(fz3*fz3*C3)

print "Lo Inductance Lz:", round(Lz,2), "uH"

### draw tracking graph

print "============Tracking graph==============="

## Defautl Unit: Hz, H, F

Crmax = Crmax*pF

Crmin = Crmin*pF

f1 = f1*MHz

f2 = f2*MHz

f3 = f3*MHz

IF = IF*MHz

Fmin = Fmin*MHz

Fmax = Fmax*MHz

# convert to H, F

Lz = Lz*uH

Lr = Lr*uH

CL = CL*pF

Cm = Cm*pF

Cp = Cp*pF

##print "Lz, Lr, CL, Cm, Cp", Lz, Lr, CL,Cm,Cp

"""

    忽略Ct:

                                                 Cp

                                                 ||

                                         .-------||---o----.

             .-------o-----.             |       ||   |    |

             |       |     |             |            |    |

             C|      |     | Cr          C|           |    |

          Lr C|  CL ---   ---         Lz C|          ---  ---

             C|     ---   ---            C|       Cm ---  --- Cz

             |       |     |             |        ,  |    |

             '-------o--,´-'              ------,-´---o----'

                      ,´                     ,-´

                      --------------------  ´

               Signal Input                  Oscllator Tank

 

"""

def ant_F(Cr):

    C = Cr + CL

    L = Lr

    return 1/(2*pi*math.sqrt(L*C))

def osc_F(Cz):

    C1 = Cz + Cm

    C = C1*Cp/(C1+Cp)

    L = Lz

    return 1/(2*pi*math.sqrt(L*C))

def ploting(diff,f, fo):

    #plt.axis([0.4, 30, 0.4, 2])

    plt.plot(CvSteps/pF, f)

    plt.plot(CvSteps/pF, fo)

    #plt.clf()

    plt.plot(CvSteps/pF, diff)

    

    

    #plt.ylim([9,10])

    #plt.show(block=False)

    plt.show()

    plt.draw()

    #plt.pause(0.01)

    

plt.ylabel("MHz/Khz(DIFF)")

plt.xlabel("pF")

plt.ion() # interactive

CvSteps = np.arange(Crmin, Crmax, 1*pF);

f = [ ant_F(Cv) for Cv in CvSteps ]

flo= [ osc_F(Cv) for Cv in CvSteps ]

diff = [ ((f1-f2)-IF)/KHz for f1, f2 in  zip(flo,f) ]

absdiff=[ abs(fx) for fx in diff ]

maxdiff=max(absdiff)

print "Max tracing error: ", maxdiff, "KHz"

### convert to MHz

f = [ fx/MHz for fx in f ]

flo = [ fx/MHz for fx in flo ]

ploting(diff, f, flo)

try:

    n=input('Anykey quit')

finally:

    exit()