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()