Code
C <- 299.792458
frequency <- 10.250
degrees <- 90
Z_o <- 50
e_r <- 9
substrate <- .6
Zo <- Z_oSam Hutchins
August 30, 2024
Using as a vehicle the “Casual Microstrip Design” series, I want to attempt to make a post that is somewhat interactive on a supposedly static web page. We shall see if I am successful in this endeavor. A reference to this series is found here in PDF format. However, I wish to concentrate simply on determining the parameters for a microstrip, while allowing user inputs for various factors and characteristics, using (Edwards and Steer 2016) formulae. As this is an experiment, I might not go into lots of detail and background effects of a particular design attempt.
Narrow strips are defined where desired impedance, \(Z_o > (44 - 2e_r) \Omega\). For determining the ratio of microstrip width (w) to substrate thickness (h), we use the formulas below. As H’ is used in the w/h formula, we determine it first:
\[H' = \frac{Z_o \sqrt{2(e_r + 1)}}{119.9} + \frac{1}{2} \left(\frac{e_r - 1}{e_r + 1}\right) \left(ln\frac{\pi}{2} + \frac{1}{e_r} ln\frac{4}{\pi}\right)\]
and then:
\[\frac{w}{h} = \left(\frac{e^{H'}}{8} - \frac{1}{4 e^{H'}}\right)^{-1}\]
where Euler’s number, e = 2.718281828, and \(e_r\) is relative permittivity1 of the board substrate. Now, to determine the effective relative permittivity:
\[ E_{eff} = \frac{e_r + 1}{2} \left[1 - \frac{1}{2H'} \left(\frac{e_r - 1}{e_r + 1}\right) \left(ln \frac{\pi}{2} + \frac{1}{e_r} ln \frac{4}{\pi} \right) \right]^{-2}\]
Now we wish to get some user inputs using the prompts as below. This is the difficult part! Well, after some investigation, I find running an interactive Shiny app, Observable JS or a htmlwidget on a static server like Netlify is virtually impossible. Attempting to use a dashboard function is like herding chickens, especially with different levels and snippets of text or code layouts. There are ways to host or run the widget elsewhere and embed the output in the page, but I don’t desire to go that route, as this is all a casual hobby for me. So for now, let’s enter some static values.
frequency <- as.numeric(readline(prompt="Desired frequency (GHz): "))
degrees <- as.numeric(readline(prompt="Desired electrical degrees: "))
Z_o <- as.numeric(readline(prompt="Input impedance: "))
e_r <- as.numeric(readline(prompt="Relative permittivity: "))
substrate <- as.numeric(readline(prompt="Substrate thickness (mm): "))
As we are unable to easily get user input, we will set some initial values for the above prompts.
We will use these for our starting point. Next, we define one function to define Eeff, considering the frequency effect.
dispersion <- function(er,Eeff,Wh,F,H) {
H <- H*0.1 # change mm to cm
P4 <- 1+2.751*(1-exp(-(er/15.916)^8))
P3 <- 0.0363*exp(-4.6*Wh)*(1-exp(-(F*H/3.87)^4.97))
P2 <- 0.33622*(1-exp(-0.03442*er))
P1 <- 0.27488+(0.6315+0.525/(1+0.157*F*H)^20)*Wh-0.065683*exp(-8.7513*Wh)
Pf <- P1*P2*((0.1844+P3*P4)*10*F*H)^1.5763
EeffF <- er-(er-Eeff)/(1+Pf)
return(EeffF)
}This is the output of the complete series of formulas.
[1] "Single Microstrip"
[1] "Narrow strip, hi-Z."
[1] "w/h: 1.058"
[1] "Wid: 0.635 Len: 2.963"
[1] "E_eff: 6.079 E_eff(f): 6.374"
[1] "Zo: 50 ohms. Zo test: 49.924 ohms. Zo test(f): 48.753 ohms."
[1] "End effect value (leo): 0.191 mm. leo(f): 0.190 mm"
This method is covered in an earlier post on this same subject. So, without active user input, we have gained nothing. More investigation is needed to determine whether it us useful to try other avenues for user input than what I’ve shown here. However, I can include the (R Core Team 2025) code to do the above calculation, running in an R environment (i.e., source(“microstrip.R”), for example). This code includes an option for coupled lines, with functions for even and odd strips, in addition to calculating a single microstrip.
print("Microstrip Calculations.")
C <- 299.792458 # lightspeed/6
#Z_o <- 376.730313461 # free space Z
#u_o <- 12.5663e-7 # free space permeability
#e_o <- 8.854187817e-12 # free space permittivity
frequency <- as.numeric(readline(prompt="Desired frequency (GHz): "))
degrees <- as.numeric(readline(prompt="Desired electrical degrees: "))
Z_o <- as.numeric(readline(prompt="Input impedance: "))
e_r <- as.numeric(readline(prompt="Relative permittivity: "))
substrate <- as.numeric(readline(prompt="Substrate thickness (mm): "))
Zo <- Z_o
dispersion <- function(er,Eeff,Wh,F,H) {
H <- H*0.1 # change mm to cm
P4 <- 1+2.751*(1-exp(-(er/15.916)^8))
P3 <- 0.0363*exp(-4.6*Wh)*(1-exp(-(F*H/3.87)^4.97))
P2 <- 0.33622*(1-exp(-0.03442*er))
P1 <- 0.27488+(0.6315+0.525/(1+0.157*F*H)^20)*Wh-0.065683*exp(-8.7513*Wh)
Pf <- P1*P2*((0.1844+P3*P4)*10*F*H)^1.5763
EeffF <- er-(er-Eeff)/(1+Pf)
return(EeffF)
}
dispersionCe <- function(u,g,er) {
#g=s/h, u=Wh,(in mm), fn=fh(in GHz)
be <- 0.564*((er-0.9)/(er+3))^0.053
v <- u*((20+g^2)/(10+g^2))+g*exp(-g)
ae <- 1+1/49 * log((v^4+(v/52)^2)/(v^4+0.432)) + 1/18.7 * log(1+(v/18.1)^3)
Eeff_e <- 0.5(er+1)+0.5(er-1)*(1+10/v)^-(ae*be)
return(Eeff_e)
}
dispersionCo <-function(u,g,er,E_eff) {
ao <- 0.7287*(E_eff-0.5(er+1))*(1-exp(-0.179*u))
bo <- 0.747*er/(0.15+er)
co <- bo-(bo-0.207)*exp(-0.414*u)
do <- 0.593+0.694*exp(-0.562*u)
Eeff_o <- (0.5*(er+1)+ao+E_eff)*exp(-co*g^do)-E_eff
}
val <- switch(menu(c("Single microstrip", "Coupled lines", "Exit")), 1, 2, 3)
if(val == 1) {
print("Single Microstrip")
lambda_o <- C/frequency # in mm
if(Z_o > (44-2*e_r)) {
H <- (Z_o * sqrt(2*(e_r+1)) / 119.9) + (1/2*((e_r-1)/(e_r+1))*(log(pi/2) + 1/e_r * log(4/pi)))
#print(paste("H:",format(round(H,3),nsmall=3)))
w_h <- ((exp(H)/8) - (1/(4*exp(H))))^-1
wid <- substrate/w_h^-1
E_eff <- ((e_r+1)/2) * (1- (1/(2*H)) * ((e_r-1)/(e_r+1)) * (log(pi/2) + (1/e_r) * log(4/pi)))^-2
E_effF <- dispersion(e_r,E_eff,w_h,frequency,substrate)
print("Narrow strip, hi-Z.")
} else {
de <- 59.95*pi^2 / (Z_o*sqrt(e_r))
w_h <- 2/pi * ((de-1) - log(2*de-1)) + ((e_r-1)/(pi*e_r)) * (log(de-1) + 0.293 - (0.517/e_r))
wid <- substrate/w_h^-1
E_eff <- (e_r+1)/2 + (e_r-1)/2 * (1+ (10*substrate/wid))^-0.555
E_effF <- dispersion(e_r,E_eff,w_h,frequency,substrate)
print("Wide strip, lo-Z.")
}
if(w_h <= 1) { # TEST: calculate if impedance matches input
Zo_test <- 60/sqrt(E_eff)*log((8*substrate/wid) + (wid/4*substrate))
Zo_testF <- 60/sqrt(E_effF)*log((8*substrate/wid) + (wid/4*substrate))
} else {
Zo_test <- ((120*pi) / ((sqrt(E_eff)*(wid/substrate + 1.393 + 0.667*log(wid/substrate + 1.444)))))
Zo_testF <- ((120*pi) / ((sqrt(E_effF)*(wid/substrate + 1.393 + 0.667*log(wid/substrate + 1.444)))))
}
print(paste("w/h:",format(round(w_h,3),nsmall=3)))
a <- 1.0+log((exp(4*log(w_h))+(w_h/52)^2))/(exp(4*log(w_h))+0.432)/49+log(1+exp(3*log(w_h/18.1)))/18.7
b <- 0.564*exp(0.053*log((e_r-0.9)/(e_r+3.0)))
c <- (e_r+1.0)/2+((e_r-1.0)/2*exp(-a*b*log(1.0+10/w_h)))
len <- degrees*((C/frequency)/sqrt(c))/360
print(paste("Wid:", format(round(wid,3),nsmall=3), "Len:", format(round(len,3),nsmall=3)))
print(paste("E_eff:",format(round(E_eff,3),nsmall=3),"E_eff(f):", format(round(E_effF,3),nsmall=3)))
# analysis
print(paste("Zo:", Z_o, "ohms. Zo test:", round(Zo_test,3), "ohms. Zo test(f):", round(Zo_testF,3),"ohms."))
l_eo <- 0.412*substrate*((E_eff+0.3)/(E_eff-0.258))*((w_h+0.262)/(w_h+0.813))
l_eoF <- 0.412*substrate*((E_effF+0.3)/(E_effF-0.258))*((w_h+0.262)/(w_h+0.813))
print(paste("End effect value (leo):",format(round(l_eo,3),nsmall=3),"mm. leo(f):",format(round(l_eoF,3),nsmall=3),"mm"))
} else if(val == 2) {
print("To access coupled line pair function, use odd filter order.")
order <- as.integer(readline(prompt="Enter filter order: "))
ripple <- as.numeric(readline(prompt="Enter ripple value: "))
# log1p(x) is ln(x), or log(x).
beta <- log1p(((cosh(ripple/17.37))/(sinh(ripple/17.37))))
y <- sinh(beta/(2*order))
Gval <- matrix(data=, nrow=3,ncol=order+1, byrow=F)
counter <- 1
while(counter <= order) {
Gval[1,counter] <- sin(((2*counter - 1)*pi)/(2*order))
Gval[2,counter] <- y^2 + (sin((counter*pi)/order))^2
counter <- counter+1
}
Gval[3,1] <- (2*Gval[1,1])/y
counter <- 2
while(counter < order+1) { # Normalized values in row 3
Gval[3,counter] <- (4*Gval[1,counter-1]*Gval[1,counter])/(Gval[2,counter-1]*Gval[3,counter-1])
counter <- counter+1
}
if(order%%2 == 1) { # Rload value
Gval[3,order+1] <- 1
} else {
Gval[3,order+1] <- (((cosh(beta/4))/(sinh(beta/4)))^2)^-1
}
#print(Gval) # test
Norm <- data.frame(Gval[3,]) # Get only row 3 subset
print("Normalized values:")
print(Norm[c(1:order),])
if(Norm[order+1,] == 1){
print(paste("Rload:",Norm[order+1,]))
} else {
rld <- sprintf("Rload: %.4f",Norm[order+1,])
print(rld)
}
if(order%%2 == 1) { # modulus to determine if order is odd number
# Coupled line pairs are always one more than the selected order.
# i.e. N3 = 4 line pairs
print("Now we do coupled line bandpass filter calculations.")
bandwidth <- as.numeric(readline(prompt="Enter bandwidth (as percent): "))
f_mean <- frequency
f_min <- f_mean - (f_mean*(bandwidth/100))
f_max <- f_mean + (f_mean*(bandwidth/100))
delta <- (f_max - f_min)/f_mean # fractional bandwidth of passband
#print(delta) # test
# admittance inverter constants
zo_j <- matrix(data=,nrow=order+1,ncol=3, byrow=F)
matNames <- colnames(zo_j) <- c("const","even","odd")
counter <- 1
zo_j[counter,1] <- sqrt((pi*delta)/(2*Norm[counter,]))
zo_j[counter,2] <- Zo*abs(1+zo_j[counter,1]+zo_j[counter,1]^2)
zo_j[counter,3] <- Zo*abs(1-zo_j[counter,1]+zo_j[counter,1]^2)
counter <- counter+1
while(counter <= order) {
zo_j[counter,1] <- (pi*delta)/(2*sqrt(Norm[counter-1,1]*Norm[counter,1]))
zo_j[counter,2] <- Zo*abs(1+zo_j[counter,1]+zo_j[counter,1]^2)
zo_j[counter,3] <- Zo*abs(1-zo_j[counter,1]+zo_j[counter,1]^2)
counter <- counter+1
}
zo_j[counter,1] <- sqrt((pi*delta)/(2*Norm[counter-1,]*Norm[counter,1]))
zo_j[counter,2] <- Zo*abs(1+zo_j[counter,1]+zo_j[counter,1]^2)
zo_j[counter,3] <- Zo*abs(1-zo_j[counter,1]+zo_j[counter,1]^2)
print("Admittance inverter constants")
print(" and Even/Odd impedance values for each pair.")
print(zo_j)
print("The even/odd pairs can be entered into PUFF as 'cline' values.")
print("Response shape is somewhat independent of frequency.")
}
} else {
print("Done.")
}So, the only take-away from this is the program to calculate the values. Have a great day and may God Bless you and yours during these late times. Bye for now!
Relative Permittivity is defined as the ratio of the actual or absolute permittivity of a medium to the absolute permittivity of vacuum.↩︎