section 1

advertisement
ANSWERS TO QUESTIONS AND EXERCISES OF THE CLIL
MODULE:
“C PROGRAMMING APPLICATIONS TO SYSTEMS
ANALYSIS”
SECTION 1
Step response of L-R linear system:
1) What is the crucial parameter that determines the accuracy of a finite difference solution of
an ordinary differential equation?
- The stepsize, that should be sufficiently small to consider the variations of
voltage/current during each step negligible. In case of first order linear systems this
means to choose a stepsize at least 10 times lower than the time constant of the system.
2) Run the C program that calculates the transient response of the L-R system for three different
values of the stepsize: τ/2, τ/10 and τ/50. Explain if and why the three solutions are different.
What’s the most accurate one?
-
The solution with τ/2 stepsize is not accurate because during each step variations in
current/voltage are significant and the incremental ratio approximation is poor. The τ/10
stepsize is more accurate, but the graph of the solution still exhibits a non-continuous
behavior. With the τ/50 stepsize it would be difficult to distinguish between the
theoretical solution and the finite difference approximation.
3) Try to change the directive #define tau (L/R) with the directive #define tau L/R (i.e. rewrite the
directive without parentheses). The program stops working correctly. Can you explain why?
-
The reason is that tau is replaced by L/R in the formula:
I[j+1]=I[j]+Dt*(VIf/L-I[j]/tau);
Hence the formula becomes:
I[j+1]=I[j]+Dt*(VIf/L-I[j]/L/R);
that is not correct because it corresponds to I[j]/(L*R) and not I[j]*R/L as it should
correctly be when tau is replaced by (L/R).
1
4) The C program of Figure 5b) asks the user to insert the values of the initial and final input
voltage values, VIi and VIf. Can you explain why in Fig. 5a) the user digits the character ‘q’
instead of a voltage value and why the program reads 0V as VIi and 5V as VIf?
-
Because the program expects a float value (%f) for the input variables VIi and VIf. When
the user digits the character ‘q’, scanf function returns the value 0 into the char variable
risp. When risp is 0, VIi and/or VIf are set to their default values, i.e. 0V for VIi and 5V
for VIf.
5) I want to find the transient response of the same circuit but with different values of L and R,
for instance L=100µH and R=5KΩ. What should I change in the C program?
-
I simply need to change the two values in the directives:
#define L 500E-6
// 500uH inductor
#define R 1E3
// 1KOhm resistor
from 500E-6 to 100E-6 and from 1E3 to 5E3. Then compile and re-run the program.
6) Try to run the C program for different values of VIi and VIf, for instance VIi=8V and
VIf=2V. What is the behavior of the output voltage waveform? Is it still correct?
-
Yes, the behavior is still correct: the result would be an inductor discharge from 8mA to
2mA. In fact, the underlying theory, and hence the equation describing inductor charging
and discharging phenomenon, is the same.
7) In Eq. 1.6 In+1 depends only on In and other known quantities. A recursive algorithm is
therefore possible. What code lines should be changed to implement the recursive algorithm?
And what are the pros. and cons. of the recursive solution?
-
Rearranging Eq. 1.6 we get:
5V
 ∆t 
Eq. A.1: I n +1 = 1 −  I n + ∆t ⋅
;
τ 
L

Therefore it is sufficient to define the recursive function curr(n) that implements Eq. A.1:
float curr(int n){
// returns the current value at discrete time instant tn=n*Dt
if (!n) return VIi/R; // if n==0 current is VIi/R
else return (Dt*VIf/L+(1-Dt/tau)*curr(n-1));
}
and to modify the instructions to write the data into the output file:
for (j=0; j<=N; j++)
fprintf(fp,"\t%.3e\t%.3f\n",j*Dt,R*curr(j));
Note that we chose to write only the output voltage in the output file. In this way we do
not need to store discrete current values or call curr(n) function more than once. In fact, if
we wanted to write both current and voltage values, we could have chosen between two
possible solutions: 1) store the current value curr(j) into a temporary variable and then use
this variable to write both current and voltage in the output file; 2) call curr(j) twice, once
to store the current value and the other to store the voltage value – this second solution is,
of course, the worse because it doubles calculation time.
The pros. of this recursive approach is that we do not need any array I[n] where to store
the data (it spares memory). The cons. is that the program is more demanding in terms of
numerical calculations and use of stack memory: therefore, the recursive program
2
execution time is longer for the same number of discrete time instants.
Here follows the complete code of the recursive program, with modified code lines
highlighted in grey color:
// LR finite difference method solution with recursive function – LRrecursive.cpp
#include <stdio.h>
// program defined input data
#define L 500E-6
// 500uH inductor
#define R 1E3
// 1KOhm resistor
#define tau (L/R)
// time constant of the system
#define tTR (5*tau) // duration of the transient fixed at 5 time constant
// function declaration
float curr (unsigned int n);
// program variables definition
FILE *fp;
// pointer to the output file
unsigned int j, N;
char risp, filename[10];
float VIi, VIf, Dt;
int main (){
// reading of user defined input data
printf("\nPlease insert step waveform initial voltage in V (0 default): ");
risp=scanf("%f",&VIi);
fflush(stdin);
if (!risp) VIi=0;
printf("Step waveform initial voltage is %.2fV\n",VIi);
printf("\nPlease insert step waveform final voltage in V (5 default): ");
risp=scanf("%f",&VIf);
fflush(stdin);
if (!risp) VIf=5;
printf("Step waveform final voltage is %.2fV\n",VIf);
printf("\nPlease insert finite difference analysis stepsize in s (%g default): ",((L/R)/10));
risp=scanf("%f",&Dt);
fflush(stdin);
if ( (!risp) || (Dt<=0) || (Dt>=(L/R)) ) Dt=(L/R)/10;
printf("Finite difference analysis stepsize is %.3g s\n",Dt);
printf("\nPlease insert the output file name\n");
scanf("%s",filename);
fflush(stdin);
printf("Press any key to start output file writing...");
getchar();
fflush(stdin);
fp=fopen(filename,"w");
// open the file in write mode
fprintf(fp,"##\t L-R output voltage waveform\n");
fprintf(fp,"##\t problem inputs: VIi=%.2fV, VIf=%.2fV, Dt=%gs, L=%gH, R=%gOhm\n##\n",VIi,VIf,Dt,L,R);
fprintf(fp,"##\tt[s]\t\tVO[V]\n");
// initialization
N=tTR/Dt+1;
if (N>1000){
N=1000;
printf("\n\nWarning: Dt is too small, I'll calculate transient response only up to %gs\n\n",N*Dt);
}
// calculate current and write voltage to output file
for (j=0; j<=N; j++) fprintf(fp,"\t%.3e\t%.3f\n",j*Dt,R*curr(j));
// close the output file
fclose(fp);
printf("\nPress any key to exit... ");
getchar();
}
// close the main
// function body
float curr (unsigned int n) // returns current value at time instant tn=n*Dt
{
if (!n) return (VIi/R);
// if n is 0 (initial instant), then current is VIi/R
else return (Dt*VIf/L+(1-Dt/tau)*curr(n-1));
}
C programming Laplace method solution:
1) Write a C program able to plot the Laplace solution of the circuit of Fig. 10 when I(0) is
different from 0A. (Suggestion: first derive the exact mathematical resolution formula by
using Laplace transform table of Appendix A, and then implement it with a C code).
-
We start from the solution of the circuit of Fig. 10 – the first part of Eq. 1.8:
3
VIf
+ L ⋅ I (0)
VIf + s ⋅ L ⋅ I (0) R VIf + s ⋅ L ⋅ I (0) VIf
1
R L ⋅ I (0)
VO ( s ) = R ⋅ ( s
) = R⋅
= ⋅
=
⋅
+ ⋅
1
1 L 
1
τ
R + sL
s ⋅ (R + sL )
L


s ⋅s + 
s ⋅s + 
s + 
 τ
 τ
 τ
where VIf is the final input voltage value (5V in Eq. 1.8) and R is the resistor (R5 in Eq.
1.8). The Laplace transform of the output voltage is the sum of two contributions: the first
contribution
VIf
τ
⋅
1
1

s ⋅s + 
 τ
−
is dependent only on VIf and τ=L/R, and its inverse
t
R L ⋅ I ( 0) R ⋅ I ( 0)
VIi
, that
⋅
=
=
1 
1 
1
L 
s +  s +  s + 
 τ  τ  τ
τ
transform is VIf ⋅ (1 − e ) . The second contribution is
−
t
τ
depends only on VIi (initial condition) and τ, and whose inverse transform is VIi ⋅ e .
Therefore, the exact output voltage waveform is the sum of the two inverse transforms
−
t
too: VO (t ) = VIf ⋅ (1 − e τ ) + VIi ⋅ e
−
t
τ
= VIf + (VIi − VIf ) ⋅ e
−
t
τ
.
This formula is valid both for inductor charging (VIi < VIf) and discharging (VIi > VIf)
and it provides correct results even when VIi and/or VIf are negative voltages. The
following C program implements the formula:
/*** LR step response – LRlaplfull.cpp ***
This program implements the L-R inductor charging transient.
Data inputs (user defined): input waveform, VIi and VIf - initial and final voltage values (in V), and the
number of data points to be plotted, N
Other inputs (program defined): component values, L (in H) and R (in Ohm)
Data elaboration: the program calculates the output voltage value at each specified data points
Data Outputs: file containing N data points reporting output voltage versus time
*/
// ANSI-C libraries included by the program
#include <stdio.h>
#include <math.h>
// program defined input data
#define L 500E-6
// 500uH inductor
#define R 1E3
// 1KOhm resistor
#define tau (L/R)
// time constant of the system
#define tTR (5*tau) // duration of the transient fixed at 5 time constant
// program variables definition
FILE *fp;
// pointer to the output file
unsigned int j, N;
char risp, filename[10];
float VIi, VIf;
int main (){
// reading of user defined input data
printf("\nPlease insert step waveform initial voltage in V (0 default): ");
risp=scanf("%f",&VIi);
fflush(stdin);
if (!risp) VIi=0;
printf("Step waveform initial voltage is %.2fV\n",VIi);
printf("\nPlease insert step waveform final voltage in V (5 default): ");
risp=scanf("%f",&VIf);
fflush(stdin);
if (!risp) VIf=5;
printf("Step waveform final voltage is %.2fV\n",VIf);
printf("\nPlease insert the number of data points to be plotted (100 default): ");
risp=scanf("%d",&N);
fflush(stdin);
if ( (!risp) || (N<=1) ) N=100;
printf("The number of data points is %d\n",N);
printf("\nPlease insert the output file name\n");
scanf("%s",filename);
fflush(stdin);
printf("Press any key to start output file writing...");
getchar();
4
fflush(stdin);
fp=fopen(filename,"w");
// open the file in write mode
fprintf(fp,"##\t L-R output voltage waveform\n");
fprintf(fp,"##\t problem inputs: VIi=%.2fV, VIf=%.2fV,L=%gH, R=%gOhm\n##\n",VIi,VIf,L,R);
fprintf(fp,"##\tt[s]\t\tVO[V]\n");
// calculate output voltage and write to output file
for (j=0; j<=N; j++) fprintf(fp,"\t%.3e\t%.3f\n",j*tTR/N,(VIf+(VIi-VIf)*exp(-(j*tTR/N)/tau)));
// close the output file
fclose(fp);
printf("\nPress any key to exit... ");
getchar();
}
2) Run the program of Fig. 13 with VIi > VIf, by simulating an inductor discharge. What is the
sign of the finite difference percentage error and where is the highest percentage error found?
Also try to use a negative voltage value for VIi and/or VIf and discuss the behavior of the
corresponding percentage error.
-
The Figure below shows with a black solid line the percentage error of the finite
difference solution for an inductor discharge with input voltage from 5V to 0V. The red
dashed line is the percentage error of the finite difference solution for an inductor charge
with input voltage from -3V to 3V.
By comparing these results with Fig. 14 where the percentage error was maximum at the
beginning of the transient it is clear that: for the step input voltage from 5V to 0V
maximum percentage error occurs at the end of the transient, where output voltage is
close to 0V; for the step input voltage from -3V to 3V maximum percentage error occurs
after 350ns from the beginning of the transient, where again output voltage is close to 0V.
In conclusion, these results demonstrate that percentage error is maximum when output
voltage value is close to 0V. In fact, Eq. 1.10 shows that Errj is higher for lower values of
VOLT[j] for the same difference (VOLT[j]-VOFE[j]).
3) Modify the C code of Fig. 13 in order to find the maximum Dt value able to guarantee a
given input accuracy. In practice, the program input is the desired level of accuracy (for
instance ±0,5%) and the output is the maximum Dt value able to guarantee the specified
accuracy.
-
The new program is essentially the same as in Fig. 13 with the difference that it needs a
new user defined input variable, Acc. The variable Dt is still used and it is initialized to
5
the value τ/c, with c=10 (c is a new unsigned int variable). The program calculates the
percentage error for each discrete time instant and stores its maximum value in the new
variable MaxErr. When MaxErr becomes lower than Acc the desired accuracy is reached
and the program prints Errj versus time in the output file; also Dt value is printed on the
screen before the program exits. On the contrary, each time MaxErr is higher than Acc c
is incremented, Dt=τ/c decreases and the percentage error calculation starts again with a
smaller value of Dt. Finally, c will be so high that MaxErr will become lower than Acc
and the program exits. However, in some cases, the percentage error remains high
however small Dt value is. This is the case when the input step waveform goes from
positive to negative voltage or vice versa. When the output voltage becomes close to 0V
the percentage error remains high even for very small Dt values. A new parameter is
added to the program to avoid excessive computational time: NMAX is the maximum
number of steps calculated by the program whatever Dt value is. This means that if the
number of steps corresponding to the Dt value is higher than NMAX, only NMAX steps
are calculated (only a portion of the total transient time is calculated). Hence, in the case
of transients from positive to negative voltage or vice versa, the program will be able to
reach the target accuracy only in the portion of the transient where the output voltage is
only positive (or negative). The C code implementing this algorithm is shown below:
// ANSI-C libraries included by the program - LRcalcDt.cpp
#include <stdio.h>
#include <math.h>
// program defined input data
#define L 500E-6
// 500uH inductor
#define R 1E3
// 1KOhm resistor
#define tau (L/R)
// time constant of the system
#define tTR (5*tau) // duration of the transient fixed at 5 time constant
#define NMAX 2000 // maximum number of data points
// functions declaration
float cfe(unsigned int n); // returns finite difference current value at instant tn=n*Dt
float clt(unsigned int n); // returns exact current value at instant tn=n*Dt
// program variables definition
FILE *fp;
// pointer to the output file
unsigned int j, N, c;
char risp, filename[10];
float VIi, VIf, Acc, Dt, Ife, Ilt, MaxErr;
int main (){
// reading of user defined input data
printf("\nPlease insert step waveform initial voltage in V (0 default): ");
risp=scanf("%f",&VIi);
fflush(stdin);
if (!risp) VIi=0;
printf("Step waveform initial voltage is %.2fV\n",VIi);
printf("\nPlease insert step waveform final voltage in V (5 default): ");
risp=scanf("%f",&VIf);
fflush(stdin);
if (!risp) VIf=5;
printf("Step waveform final voltage is %.2fV\n",VIf);
printf("\nPlease insert desired accuracy in %c (5%c default): ",37,37);
risp=scanf("%f",&Acc);
fflush(stdin);
if ( (!risp) || (Acc<1e-10) || (Acc>=30) ) Acc=5;
printf("Finite difference analysis accuracy is fixed to %.2f\%c\n",Acc,37);
// initialization
c=9;
do{
c++, MaxErr=0, Dt=tau/c, N=tTR/Dt+1;
if (N>NMAX){
N= NMAX;
printf("\n\nWarning: Dt is too small, calculation stops at %gs\n\n",N*Dt);
}
for (j=1; j<=N; j++){
Ilt=clt(j), Ife=cfe(j);
if (fabs(Ilt)<1e-18) Ilt=fabs(Ilt)+1e-18;
6
if (MaxErr < fabs(100*(Ilt-Ife)/Ilt)) MaxErr=fabs(100*(Ilt-Ife)/Ilt);
}
}while(MaxErr > Acc);
printf("\nDesired accuracy is reached for c=%d corresponding to Dt=%.3gs\n",c,Dt);
printf("Maximum percentage error is %.2f%c\n",MaxErr,37);
printf("\nPlease insert the output file name\n");
scanf("%s",filename);
fflush(stdin);
printf("Press any key to start output file writing...");
getchar();
fflush(stdin);
fp=fopen(filename,"w");
// open the file in write mode
fprintf(fp,"##\t L-R output voltage waveform\n");
fprintf(fp,"##\t problem inputs: VIi=%.2fV, VIf=%.2fV, Dt=%gs, L=%gH, R=%gOhm\n",VIi,VIf,Dt,L,R);
fprintf(fp,"##\t outputs: percentage error Err, exact output voltage VOlt and numerical output voltage VOfe\n##\n");
fprintf(fp,"##\tt[s]\t\tErr[%c]\t\tVOlt[V]\t\tVOfe[V]\n",37);
fprintf(fp,"\t%.2e\t%.2f\t\t%.3f\t\t%.3f\n",0.0,0.0,VIi,VIi);
for (j=1; j<=N; j++){
Ilt=clt(j), Ife=cfe(j); // current values stored in temporary variables
if (fabs(Ilt)<1e-18) Ilt=fabs(Ilt)+1e-18;
fprintf(fp,"\t%.2e\t%.2f\t\t%.3f\t\t%.3f\n",j*Dt,100*(Ilt-Ife)/Ilt,R*Ilt,R*Ife);
}
// close the output file
fclose(fp);
printf("\nPress any key to exit... ");
getchar();
}
// functions body
float cfe(unsigned int n){
// returns the finite difference current value at instant tn=n*Dt
if (!n) return (VIi/R); // if n==0 current is VIi/R
else return (Dt*VIf/L+(1-Dt/tau)*cfe(n-1));
}
float clt(unsigned int n){
// returns the exact current value at instant tn=n*Dt
return ((VIf+(VIi-VIf)*exp(-(n*Dt/tau)))/R);
}
Try to run this program in these three different cases:
• Input voltage from 6V to 1V
• Input voltage from 1V to 6V
• Input voltage from 3V to -2V
In all 3 cases the total voltage swing is 5V but the percentage error behavior versus time
is quite different…
4) Write a C code similar to that of Fig. 13 but whose objective is the evaluation of the absolute
error of the finite difference method, i.e. ErAj=VOLT[j]-VOFE[j]. Then run the new program
with the same inputs as in Fig. 14 and for the same three different values of Dt of Fig. 14,
namely Dt=τ/10 (50ns), Dt=τ/25 (20ns) and Dt=τ/50 (10ns); finally plot the resulting
absolute errors versus time as in Fig. 14. What is the difference between absolute and
percentage errors? Explain the differences between the plots of absolute and percentage
errors versus time.
-
The program to write is essentially the same as in Fig. 13 where the following code lines:
for (j=1; j<=N; j++){
Ilt=clt(j), Ife=cfe(j); // current values stored in temporary variables
if (Ilt==0) Ilt+=1e-18;
fprintf(fp,"\t%.2e\t%.2f\t\t%.3f\t\t%.3f\n",j*Dt,100*(Ilt-Ife)/Ilt,R*Ilt,R*Ife);
}
are replaced by:
for (j=1; j<=N; j++){
7
Ilt=clt(j), Ife=cfe(j); // current values stored in temporary variables
fprintf(fp,"\t%.2e\t%.3f\t\t%.3f\t\t%.3f\n",j*Dt,R*(Ilt-Ife),R*Ilt,R*Ife);
}
The C source code LRabs.cpp contains the whole program code. The results of the three
runs are shown in the Figure below: the maximum absolute error (negative because VOLT
< VOFE) is below 20mV for Dt=10ns, increases up to 38mV for Dt=20ns and is close to
0,1V when Dt=50ns (τ/10). Another important observation is that maximum error occurs
at about t=500ns=τ in all three cases. This is quite different from the results of Fig. 14
where the maximum percentage error occurs immediately after the beginning of the
transient. In fact, percentage error is higher when output voltage is close to 0V for a given
value of absolute error. When VOLT[j] is close to 0V, even small differences between
VOLT[j] and VOFE[j] may result in a high percentage error. On the other hand, at
t=500ns=τ the difference between VOLT[j] and VOFE[j] is maximum, but VOLT[j] is high,
close to 3,16V: the result is a low percentage error.
In actual numerical simulators you can usually specify both percentage and absolute
error, according to what kind of precision is desired.
5) Write the C code to calculate the step response of the following first-order linear systems by
using the Laplace transform or the finite difference method: R-L system, R-C system, C-R
system.
- The four possible first-order linear systems we can analyze with the finite difference or
Laplace methods are shown in the next page. We have already investigated the L-R
system in detail. Now just as examples we’ll apply the Laplace method to the R-L system
and the finite difference method to the C-R system.
By transforming the R-L system into the Laplace domain, for t ≥ 0 we obtain the circuit
shown in the next page, where I0 is the current value at instant t=0 (I0) and VIf is the final
input voltage value. Note I0=VIi/R depends on initial input voltage value. The solution of
the circuit in the Laplace domain provides:
VIf
+ L ⋅ I0
VIf
s
VIi
VIf
VIi
VO (s ) = s ⋅ L ⋅ I (s ) − L ⋅ I 0 = s ⋅ L ⋅ s
− L ⋅ I0 =
+ VIi ⋅ τ
− L⋅
=
−
1
1
1
1
R+ s⋅L
R
s+
s+
s+
s+
τ
τ
τ
τ
that is the sum of two contributions, the first dependent on VIf and τ and the second
8
−
t
dependent on VIi and τ. The inverse transform of the first term provides VIf ⋅ e τ , while
−
t
the inverse transform of the second term provides − VIi ⋅ e τ . Hence, the solution of the
−
t
system is the sum of the two inverse transforms: VO (t ) = (VIf − VIi) ⋅ e τ .
L
100µ
VI(t)
VO(t)
R
L-R system
1K
L
VI(t)
1K
R
VO(t)
100µ
C
C
1K
VI(t)
R-L system
R
R-C system
VO(t)
10µ
VI(t)
10µ
R
1K
VO(t)
C-R system
R
s*L
VI(t)
VO(t)
VIf/s
L*I0
Note that after 5 time constants VO(t) is close to 0V and the circuit goes into steady state
condition. The C program implementing the Laplace solution of the system is the same as
the code of Fig. 11 with the only difference that the code line:
for (j=0; j<=N; j++) fprintf(fp,"\t%.3e\t%.3f\n",j*tTR/N,VIf*(1-exp(-(j*tTR/N)/tau)));
is replaced by:
for (j=0; j<=N; j++) fprintf(fp,"\t%.3e\t%.3f\n",j*tTR/N,(VIf-VIi)*exp(-(j*tTR/N)/tau));
The whole C source code can be found in the file RLlapl.cpp. Now, let’s move to the
finite difference calculation for the C-R system. The capacitor is described by the
differential equation I (t ) = C ⋅
dVC
, where I(t) is the circuit current equal to VO(t)/R and
dt
VC(t) is the voltage drop across the capacitor, i.e. VC(t)= VI(t)-VO(t). Therefore the
characteristic equation of the capacitor can be rewritten as:
9
VO (t )
dV
= −C ⋅ O , since for t≥0 VI is constant, hence its derivative is zero. Rearranging the
R
dt
dVO VO (t )
equation we get
+
= 0 , where τ=R*C is the time constant of the circuit. By
dt
τ
using the finite difference method where at each discrete time instant tj=j*Dt=j*∆t a
discrete output voltage value VOj corresponds, we can approximate the derivative with the
incremental ratio:
dVO ∆VO VOn+1 − VOn
≈
=
.
dt
∆t
t n+1 − t n
The smaller the stepsize Dt=∆t the better this approximation is. By solving the equation
τ − ∆t
for VOn+1 we find the Euler resolution formula: VOn +1 =
⋅ VOn . If VO0 is known, we
τ
can calculate VO1 by using the resolution formula; once VO1 is known VO2 can be
calculated and so on until the end of the transient is reached. So we need to find the initial
condition VO0 to solve the problem with the finite difference method. VO0 calculation
derives from the law of continuity of the voltage drop across capacitors. At the instant
t=0- (just before the step on input voltage) VC(0-)=VIi-0V=VIi. At the instant t=0+ (just
after the step on input voltage) VC(0+)=VIf-VO(0+)=VIf-VO0. Since VC(t) should be a
continuous function of time, i.e. VC(0-)=VC(0+), we get VIi=VIf-VO0, from which
VO0=VIf-VIi. We can now write the C program by solving the C-R system with the finite
element method: it is the same program as in Fig. 5b where the array I[1000] is replaced
by the array VO[1000] and the following code lines:
I[0]=VIi/R, j=0, N=tTR/Dt+1;
fprintf(fp,"\t%.3e\t%.3f\t\t%.3g\n",j*Dt,R*I[j],I[j]);
I[j+1]=I[j]+Dt*(VIf/L-I[j]/tau);
are replaced by:
VO[0]=VIf-VIi, j=0, N=tTR/Dt+1;
fprintf(fp,"\t%.3e\t%.3f\t\t%.3g\n",j*Dt,VO[j],VO[j]/R);
VO[j+1]=(1-Dt/tau)*VO[j];
The whole C code can be found in the file CRfindiff.cpp. Try and enjoy it for various
values of VIi and VIf.
SECTION 2
1) Draw a plot of the Dirac pulse waveform. Can we generate a Dirac pulse in the lab?
-
The Dirac pulse waveform is a voltage equal to 0 for any value of time different from 0
and equal to +∞ when t=0 and is indicated with the symbol ∂(t). The area below the Dirac
pulse is equal to 1Vs (1 Volt times second). In a voltage versus time axis it is a horizontal
line coincident with the x-axis with a vertical line in correspondence of t=0 (see
Appendix A for its plot). We cannot generate unbounded physical quantities in nature not
even with an instant duration. The Dirac pulse is only an abstract concept with important
10
mathematical properties. In the lab we can only approximate the Dirac pulse with a pulse
with a high amplitude (but bounded) and a small width – however finite.
2) Can I use the finite difference method to find the Dirac pulse transient response of an RC
system? And the RC transient response to a bounded pulse of finite width t0?
-
The finite difference method cannot be used to find the Dirac transient response of an RC
circuit. In fact, the Dirac pulse has 0 width, so we cannot divide it into a number of
smaller steps. The finite difference method can be used with real pulse inputs, with a
bounded amplitude and a finite width. However, if the width t0 of the pulse is much
smaller than the time constant τ of the system, the stepsize Dt should be chosen much
smaller than t0 in order to get a good approximation of the real transient response. So it is
convenient to divide the pulse response into two different time intervals. The first time
interval is when the pulse is active and its width is t0: this time interval is divided into
steps Dt=t0/20 in order to approximate the pulse transient response correctly. At the end
of the first time interval the RC output voltage will have reached a finite value V1. The
second time interval is when the pulse is no more applied: the RC output voltage starts to
discharge beginning from the previously calculated voltage V1. The analysis of this
second time interval can be done by choosing Dt=τ/20.
If, on the other hand, t0 is comparable with or greater than the time constant τ of the
system, there is no need to divide the simulation into two time intervals and the choice of
the stepsize Dt can be based solely on τ (Dt=τ/20 may give a good approximation).
3) Implement an LR linear system in the lab and apply a bounded pulse of amplitude V0 and
width t0 to its input. What output waveform do you expect? Is there current continuity in the
inductor?
-
The LR circuit behaves exactly as the RC. So we may have two different cases:
1. t0 much smaller than τ: the LR step response can be approximated with the Dirac pulse
response: VO (t ) =
V0 ⋅ t 0
−
t
τ
⋅ e , where V0*t0 is the area below the input pulse waveform.
τ
Remember this formula is only an approximation of the actual pulse response of the
system; it provides VO(0+)=V0*t0/τ and hence I(0+)= V0*t0/(R*τ). Since I(0-)=0 it seems
there is a discontinuity in the inductor current. Actually, VO(0+) starts from 0 and reaches
the V0*t0/τ value during the time interval [0,t0] where the pulse is applied. So there isn’t
any current discontinuity. Simply, since t0 is much smaller than τ, we can at all practical
purposes draw the waveform as if it started directly from the V0*t0/τ value at t=0 instead
of t=t0.
2. t0 comparable with or much greater than τ: during the time interval [0,t0] where the pulse
−
t
is applied VO (t ) = V0 ⋅ (1 − e τ ) ; hence, at the end of the pulse VO has reached the value:
VO (t 0 ) = V0 ⋅ (1 − e
−
t0
τ
) . After the pulse is finished VO(t) starts discharging from the initial
11
voltage VO(t0), hence VO (t ) = V0 ⋅ (1 − e
−
t0
τ
)⋅e
−
t −t0
τ
. There is no current discontinuity in the
inductor and the total transient time is t0+5*τ.
4) Explain the reason why the step response of a CL system features damped oscillations. What
would the step response of an ideal CL system be?
-
From the Laplace transform table of Appendix A the step response of an ideal CL circuit
is Vf*cos(wn*t), where Vf is the step input voltage final value and ω n =
1
L⋅C
is the
natural angular frequency of the circuit. However, in an actual CL system realized in the
lab the capacitor and the inductor feature parasitic effects. In particular, the capacitor has
an ESR (Equivalent Series Resistance) of a few Ω. The inductor features a series
resistance of a few Ω, too. So the actual circuit can be approximated as an RCL series
circuit characterized by the damping factor ζ =
R < 2⋅
R C
⋅
. For small values of R (i.e. for
2
L
L
, corresponding to ζ<1) the system works in underdamped conditions and
C
oscillations are present. This is the typical case of any CL system where C value is
usually much smaller than L. However, a CL circuit where the value of C is higher than
the value of L may be working in overdamped condition (ζ>1): in this case oscillations
won’t be present and the step response of the CL circuit would resemble that of a CR
circuit.
5) Write a C program that calculates the step response of an RCL series system. The program
reads the values of the circuit components (R, C and L) and the final value of the step input
voltage (Vf). The output of the program should be a text file that contains the waveforms of
VR(t), VC(t) and VL(t) (the voltages across the resistor, capacitor and inductor, respectively).
Finally, the text file should be imported in Excel to draw the corresponding waveforms.
-
It is convenient to use the C library tranlib.cpp of Appendix B, that already contains the
function:
void RCLstep(float R, float L, float C, float Vf, int Np);
this function generates a file RCLstep.txt containing Np data points describing the step
response of the RCL system (including three columns, VR(t), VC(t) and VL(t) that are the
voltages across the resistor, capacitor and inductor, respectively). So the algorithm is
extremely simple: it is sufficient to read R, C, L and Vf values and then call RCLstep()
function. You can define Np in the program and remember to include the library
tranlib.cpp with its full path. Here’s the C code:
/*** RCL series circuit step response: RCL_series.cpp ***
This program calculates the RCL series circuit step response.
Data inputs (user defined): R, C, L values + VIf final step voltage value.
Parameters (program defined): Np, the number of data points
Data elaboration: it is performed by routine RCLstep() defined in tranlib.cpp library
12
Data Outputs: file containing Np data points reporting circuit output voltages versus time */
// ANSI-C libraries included by the program
#include <stdio.h>
// User defined libraries included by the program - with full library path
#include "C:\Scuola\ANNO 2010-2011\CLIL IV SISTEMI\tranlib.cpp"
// program defined parameters
#define Npoints 200
// number of data points to be plotted in the output file
// program variables definition
char risp;
float R, C, L, Vf;
int main (){
// reading of user defined input data
printf("\nPlease insert step waveform final voltage in V (5 default): ");
risp=scanf("%f",&Vf);
fflush(stdin);
if (!risp) Vf=5;
printf("Step waveform final voltage is %.2fV\n",Vf);
printf("\nPlease insert circuit resistor value R in Ohm (1E3 default): ");
risp=scanf("%f",&R);
fflush(stdin);
if (!risp) R=1E3;
printf("Circuit resistor value is %g Ohm\n",R);
printf("\nPlease insert circuit capacitor value C in F (100E-9 default): ");
risp=scanf("%f",&C);
fflush(stdin);
if (!risp) C=100E-9;
printf("Circuit capacitor value is %gF\n",C);
printf("\nPlease insert circuit inductor value L in H (500E-6 default): ");
risp=scanf("%f",&L);
fflush(stdin);
if (!risp) L=500E-6;
printf("Circuit inductor value is %gH\n",L);
printf("Press any key to start output file writing...");
getchar();
fflush(stdin);
// CALL to function RCLstep() to calculate output voltages and write to output file
RCLstep(R,L,C,Vf,Npoints);
printf("\nPress any key to exit... ");
getchar();
}
After importing the output file RCLstep.txt into an MS Excel spreadsheet, the plot
shown in the next page is obtained (the input values are Vf=6V, R=300Ω, C=10nF and
L=1mH, corresponding to a damping factor of 0,47). VL starts from 6V and after two
bounces it goes to 0V. VR features two bounces too: it starts from 0V, goes to a
maximum of about 3V and then decreases back to 0V. VC final value is 6V: it starts
from 0, goes to a maximum of about 7V and then decreases back to 6V final value.
Different waveforms are obtained for different values of circuit components
depending on the values of the damping factor and of the natural angular frequency of
the system.
13
8
7
VC
6
5
VL
4
3
2
1
VR
0
-1
-2
0
0,000005
0,00001
0,000015
0,00002
0,000025
0,00003
0,000035
6) Write a C program that asks the user what kind of linear circuit (system) and input stimulus
he wants to solve among the options shown in Table 1. The program output is a text file
containing the waveform of the desired transient response of the selected system.
-
The user can select among the fifteen possibilities shown in Table 1. A function in the
library tranlib.cpp corresponds to one of the possible user’s choices. Table 1 is made of 7
rows for three columns. We may associate an integer number to each row of the Table,
let’s call it j, from 1 to 7 (j=1 corresponds to RC, j=2 to CR, j=3 to LR, j=4 to RL, j=5 to
LC, j=6 to CL and j=7 to RCL series). Another integer number, let’s call it k, is
associated to one of the three columns (k=1 corresponds to the Dirac pulse, k=2 to the
step input voltage and k=3 to the ramp input voltage). The program asks the user what
value of j and k he wants to select and, based on the user’s choice, it calls the
corresponding function in the library tranlib.cpp. However, the program should handle
cases where the user, by mistake, selects a number outside the allowed range (j>7 or
k>3). There are also allowed values of (j, k) that do not correspond to any function in
tranlib.cpp: for instance j=2 and k=1 corresponds to the CR circuit with Dirac pulse
input, but there is no such function in tranlib.cpp. The program should handle such cases
too. Once the user has selected two allowed values of (j, k) the program should call the
corresponding function in tranlib.cpp. We may use the selection structure to do that, as in
the following pseudo-code:
if ((j==1) && (k==1)){ // RC, Dirac
read R, C;
call RC dirac(R,C,Np);
} else {
if (…..){
….
}
The use of nested if(){…}else{…} can be quite complex. When multiple selections are
possible it is better to use only the if(){…} structure, as in the following pseudo-code:
check=0;
if ((j==1) && (k==1)){ // RC, Dirac
read R, C;
call RC dirac(R,C,Np);
14
check=1;
}
if ((j==1) && (k==2)){ // RC, Step
read R, C, Vf;
call RC step(R,C,Vf,Np);
check=1;
}
……….
The variable check is used to know if one of the correct possible choices has been
selected. At the end of the if structures check may be 0 or 1: if check is 0 it means the
user’s selection doesn’t correspond to any function in tranlib.cpp, and the program may
display: “sorry, I’m not able to calculate the transient response you requested”; if check
is 1 it means the program already calculated the requested transient response and the
program can exit without any warning. However, C provides a much simpler structure to
deal with multiple selections: the switch(){case: …} structure. It works like this:
switch(expression){
case value1:
sequence of instructions 1;
// break;
// if not commented the program exits the switch after sequence of instructions 1 execution
case value2:
sequence of instructions 2;
// break; // if not commented the program exits the switch after sequence of instructions 2 execution
default:
sequence of default instructions;
}
If the expression integer value is equal to one of the values in the case clause then the
program starts executing all the instructions following that case clause. For instance, in
the example above if expression is equal to value2 the program starts to execute the
sequence of instructions2 and all the following included the default one. The break
instructions in each case clause should be used when only one sequence of instructions is
to be executed. Can we use this structure in our program? The answer is yes, but we need
to translate our two user inputs (j and k) into a single integer expression. To this purpose
we may define a new integer variable, jk=j*10+k. When (j,k)=(1,1) then jk=11 and we
will call RCdirac() function; when (j,k)=(1,2) then jk=12 and we will call RCstep()
function, and so on. If jk value does not correspond to any function in tranlib.cpp then
we’ll print the following on the PC screen: “Sorry but you selected a not existing
function” in the default case. Note that since for each selection we need to call only one
function, we’ll use the break instruction in each case of the switch structure. Here’s the
complete C program:
/*** linear circuits transient responses - alltran.cpp ***
This program calculates a linear circuit transient response based on the functions contained
in library tranlib.cpp.
Data inputs (user defined): j defines the kind of circuit, k defines the kind of stimulus
depending on the j k user’s selection the program will ask the required circuit and stimuli parameters
Other Parameters (program defined): Npoints, the number of data points
Data elaboration: it is performed by the routines in tranlib.cpp library
Data Outputs: file containing Np data points reporting circuit output voltages versus time */
15
// ANSI-C libraries included by the program
#include <stdio.h>
// User defined libraries included by the program - with full library path
#include "C:\Scuola\ANNO 2010-2011\CLIL IV SISTEMI\tranlib.cpp"
// program defined functions
void readpar(bool bR, bool bL, bool bC, bool bVf, bool bP);
// program defined parameters
#define Npoints 200 // number of data points to be plotted in the output file
// program variables definition
int j, k, jk;
char risp;
float R, C, L, Vf, P;
int main (){
do{ //outer cycle to enable the calculation of many transients with only one program call
// reading of user’s defined input data
printf("\nPlease insert the kind of circuit whose transient response you want to calculate ");
printf("\n1 for RC\n2 for CR\n3 for LR\n4 for RL\n5 for LC\n6 for CL\n7 for RCL series ");
printf("\nA different number corresponds to the default selection: RC circuit ");
scanf("%d",&j);
fflush(stdin);
if ((j<0)||(j>7)) j=1; // RC circuit is default
printf("\nYou chose j=%d\n",j);
printf("\nPlease insert the kind of stimulus ");
printf("\n1 for Dirac pulse\n2 for step input\n3 for ramp input ");
printf("\nA different number corresponds to the default selection: step input ");
scanf("%d",&k);
fflush(stdin);
if ((k<0)||(k>3)) k=2; // step input is default
printf("\nYou chose k=%d\n",k);
jk=j*10+k;
switch (jk){
case(11):
printf("\nYou chose RC circuit with Dirac stimulus, RCdirac.txt is your output file\n");
readpar(1,0,1,0,0); // read R and C values
RCdirac(R,C,Npoints); // calculation and generation of output file
break;
case(12):
printf("\nYou chose RC circuit with step stimulus, RCstep.txt is your output file\n");
readpar(1,0,1,1,0); // read R, C and Vf values
RCstep(R,C,Vf,Npoints); // calculation and generation of output file
break;
case(13):
printf("\nYou chose RC circuit with ramp stimulus, RCramp.txt is your output file\n");
readpar(1,0,1,0,1); // read R, C and P values
RCramp(R,C,P,Npoints); // calculation and generation of output file
break;
case(22):
printf("\nYou chose CR circuit with step stimulus, CRstep.txt is your output file\n");
readpar(1,0,1,1,0); // read R, C and Vf values
CRstep(R,C,Vf,Npoints); // calculation and generation of output file
break;
case(23):
printf("\nYou chose CR circuit with ramp stimulus, CRramp.txt is your output file\n");
readpar(1,0,1,0,1); // read R, C and P values
CRramp(R,C,P,Npoints); // calculation and generation of output file
break;
case(31):
printf("\nYou chose LR circuit with Dirac pulse stimulus, LRdirac.txt is your output file\n");
readpar(1,1,0,0,0); // read R and L values
LRdirac(R,L,Npoints); // calculation and generation of output file
break;
16
case(32):
printf("\nYou chose LR circuit with step stimulus, LRstep.txt is your output file\n");
readpar(1,1,0,1,0); // read R, L and Vf values
LRstep(R,L,Vf,Npoints); // calculation and generation of output file
break;
case(33):
printf("\nYou chose LR circuit with ramp stimulus, LRramp.txt is your output file\n");
readpar(1,1,0,0,1); // read R, L and P values
LRramp(R,L,P,Npoints); // calculation and generation of output file
break;
case(42):
printf("\nYou chose RL circuit with step stimulus, RLstep.txt is your output file\n");
readpar(1,1,0,1,0); // read R, L and Vf values
RLstep(R,L,Vf,Npoints); // calculation and generation of output file
break;
case(43):
printf("\nYou chose RL circuit with ramp stimulus, RLramp.txt is your output file\n");
readpar(1,1,0,0,1); // read R, L and P values
RLramp(R,L,P,Npoints); // calculation and generation of output file
break;
case(51):
printf("\nYou chose LC circuit with Dirac pulse stimulus, LCdirac.txt is your output file\n");
readpar(0,1,1,0,0); // read L and C values
LCdirac(L,C,Npoints); // calculation and generation of output file
break;
case(52):
printf("\nYou chose LC circuit with step stimulus, LCstep.txt is your output file\n");
readpar(0,1,1,1,0); // read L, C and Vf values
LCstep(L,C,Vf,Npoints); // calculation and generation of output file
break;
case(62):
printf("\nYou chose CL circuit with step stimulus, CLstep.txt is your output file\n");
readpar(0,1,1,1,0); // read L, C and Vf values
CLstep(L,C,Vf,Npoints); // calculation and generation of output file
break;
case(63):
printf("\nYou chose CL circuit with ramp stimulus, CLramp.txt is your output file\n");
readpar(0,1,1,0,1); // read L, C and P values
CLramp(L,C,P,Npoints); // calculation and generation of output file
break;
case(72):
printf("\nYou chose RCL series circuit with step stimulus, RCLstep.txt is your output file\n");
readpar(1,1,1,1,0); // read R, C, L and P values
RCLstep(R,L,C,Vf,Npoints); // calculation and generation of output file
break;
default:
printf("\nSorry, there is no function for the combination of circuit/stimulus you chose\n");
}
printf("\n\nWould You like to calculate the transient response of another system? (Y/N)");
scanf("%c",&risp);
fflush(stdin);
}while((risp=='y')||(risp=='Y'));
printf("\nPress any key to exit... ");
getchar();
}
void readpar(bool bR, bool bL, bool bC, bool bVf, bool bP){
if(bR){
printf("\nPlease insert circuit resistor value R in Ohm (1E3 default): ");
risp=scanf("%f",&R);
fflush(stdin);
17
if (!risp) R=1E3;
printf("Circuit resistor value is %g Ohm\n",R);
}
if(bC){
printf("\nPlease insert circuit capacitor value C in F (100E-9 default): ");
risp=scanf("%f",&C);
fflush(stdin);
if (!risp) C=100E-9;
printf("Circuit capacitor value is %gF\n",C);
}
if(bL){
printf("\nPlease insert circuit inductor value L in H (500E-6 default): ");
risp=scanf("%f",&L);
fflush(stdin);
if (!risp) L=500E-6;
printf("Circuit inductor value is %gH\n",L);
}
if(bVf){
printf("\nPlease insert step waveform final voltage in V (5 default): ");
risp=scanf("%f",&Vf);
fflush(stdin);
if (!risp) Vf=5;
printf("Step waveform final voltage is %.2fV\n",Vf);
}
if(bP){
printf("\nPlease insert ramp waveform slope in V/s (1E5 default): ");
risp=scanf("%f",&P);
fflush(stdin);
if (!risp) P=1E5;
printf("Ramp waveform slope is %gV/s\n",P);
}
}
In addition to the explanation provided above, the program proposed contains an
external do{…}while cycle to allow an unlimited number of transient responses
calculations just with a single program call. In fact, with only one program call all
waveforms of Appendix A have been calculated and then imported into an MS Excel
worksheet. Another important feature of the program is the internal function
readpar(), used to read circuit and stimuli variables. In fact, the number and kind of
circuit and stimuli variables to enter as inputs depends on the user’s choice. readpar()
has 5 input parameters of Boolean type (bool), namely bR, bL, bC, bVf, bP. When one
of these Boolean parameters is true (i.e. value 1) readpar() reads the corresponding
input variable, otherwise the corresponding input variable is skipped. For instance, the
function call readpar(1,0,1,1,0) asks the user to insert R value (bR=1), C value (bC=1)
and Vf value (bVf=1), while it skips L and P values (bL=0 and bP=0) - note R, C, L,
Vf and P variables are global variables. Hence, depending on user’s input, it is
sufficient to call readpar() with the required set of Boolean parameters values to read
the required stimuli and circuit variables.
18
SECTION 3
Finite state machines – questions and exercises
1) Explain all the optimization performed to pass from the C program of Figure 37 to the one of
Figure 38.
-
The first optimization is the use of a single variable q (Boolean type) to identify the two
possible states S1 and S2 of the system. Each transition of state can then be done with a
single instruction, q=0 to pass from S1 to S2 and q=1 to pass from S2 to S1. The second
optimization is to merge two consecutive if structures in a single if structure:
if (S1){
if (x1){
…
}
}
if(q && x1){
…
}
The third and last optimization is similar to the second one with the additional
simplification of removing a redundant condition:
if (S2){
if (!x1){
…
}
}
if((!q) && (!x1)){
…
}
if(!x1){
…
}
In fact, when x1 is 0 we need to pass to state S1 whatever the state we are in.
2) With your PC simulate a finite state machine able to recognize one name made of four
letters, such as for instance MARY. In practice the system should recognize a sequence of
characters (for instance ‘M’, ‘A’, ‘R’ and ‘Y’) and then print Hello Mary! on screen.
-
Since the name to be recognized can be anyone, there could also be cases where the same
character is found two consecutive times, as for instance in “Anne”. So we must be sure
each character is processed only once: whenever function _kbhit() returns 1 a character is
read and only 1 state transition is performed; then the next character is read and so on. If
one of the characters is not in the sequence, the machine goes back to the initial state.
Note the characters are case sensitive, i.e. it makes a difference if they are upper or lower
case. For instance, if the name to be recognized is “Anne”, and the sequence of input
characters is anneanAnneanneanAnnean, Hello Anne! will be printed only twice on the
screen. Here follows the State Graph of the machine:
ch!=name[3]/ch=name[0]/cls ch=name[1]/-
ch!=name[0]/S1
S2
ch=name[2]/S3
S4
ch!=name[1]/ch!=name[2]/ch=name[3]/print ”Hello name!”
19
A Mealy State Graph has been preferred: each state transition is associated with an action
(or no action when the symbol – is used). There are only two possible actions: cls means
clear screen, print is the instruction to display the greeting on the screen. The name to be
recognized is defined as a constant string in the program (name). Within the infinite for
cycle, a while(){…} cycle with break instructions has been used in order to allow only a
single state transition for each character. Here’s the program code:
// Program to recognize a sequence of 4 characters – namerecognition.cpp
#include <stdio.h>
#include <conio.h>
#include <iostream>
// 4 characters sequence to be recognized
#define name "Anne"
// program variables definition
char ch;
bool S1=1, S2=0, S3=0, S4=0;
// initial state is S1
int main (){
for(;;){
// finite state machine working cycle
while (_kbhit()){ // the state machine processes only 1 character at a time
ch=_getch(); // read the character removing it from the input buffer
if (S1){
if(ch==name[0]){
system("cls"); // clear screen instruction
S1=0, S2=1;
}
break;
// only 1 state transition for each character
}
if (S2){
if(ch==name[1]){
S2=0, S3=1;
} else {
S2=0, S1=1;
}
break;
// only 1 state transition for each character
}
if (S3){
if (ch==name[2]){
S3=0, S4=1;
} else {
S3=0, S1=1;
}
// only 1 state transition for each character
break;
}
if (S4){
if (ch==name[3]) printf("\nHello %s!",name);
S4=0, S1=1;
// go to initial state
break;
}
// only 1 state transition for each character
} // close while
}
// close for cycle
}
// close main
3) With your PC simulate a finite state machine able to control the automatic gate of your
house.
-
The system digital inputs are: x1 – corresponding to the remote control button; x2 –
corresponding to the gate fully open sensor signal; x3 – corresponding to the gate fully
closed sensor signal; x4 – corresponding to the “someone passing” sensor signal. There is
20
another digital input, namely x5, corresponding to the end of the wait time and whose
meaning will be explained later. System initial state S1 is when the gate is still and fully
closed. The system can move from this state only if the button of the remote control is
pushed (x1=1): the gate starts opening. In our system the only way to stop the opening of
the gate is the x2 signal coming from the proximity sensor detecting the gate fully open
condition (so the gate always reaches the fully open state S2). When x2 becomes 1 the
gate stops and a timer starts. There are two possible inputs that may force the gate
closure: the timer expires (x5 input becomes 1) or the remote control button is pushed (x1
becomes 1). However, in both cases the gate starts closing only if no one is passing
through the gate (x4=0). While the gate is closing, the gate stops closing if someone is
passing through the gate (x4=1), and returns to the initial state where it waits for x1=1
(remote control button pressure) to re-open again. This is the Mealy State Graph for our
system:
10xxx/open
0xxxx/-
x1,x2,x3,x4,x5/actions
S2
S1
x0xxx/Si
xxx1x/stop
0x1xx/stop
01xxx/stop,set timer
1xx0x/close
xx00x/-
S4
S3
0xx00/timer--
xxx01/close
xxx1x/set timer
Again, in our PC simulation we cannot handle cases where inputs can be changed
simultaneously because we are forced by _kbhit() instruction to handle a character at a
time. This simplifies our design considerably, while in actual life we may have a gate
fully open signal simultaneously with a remote control button pressure. However, the
State Graph of the system is able to handle these situations, too. This is the C program
that simulates our State Graph:
// automatic gate simulation - automaticgate.cpp
#include <stdio.h>
#include <conio.h>
// program parameters
#define WAITCYCLES 300000
// number of cycles to wait before closing the gate
// program variables definition
unsigned long int timer; // counter variable defining the gate open wait time
bool x1=0, x2=0, x3=0, x4=0, S1=1, S2=0, S3=0, S4=0;
char ch;
int main (){
printf("\nMEANINGS OF KEYS:");
printf("\nB or b is the remote control button"); // x1 input
printf("\nO or o is the gate fully open sensor signal"); // x2 input
printf("\nC or c is the gate fully closed sensor signal"); // x3 input
printf("\nP or p is the 'someone passing' sensor signal\n"); // x4 input
for(;;){ // finite state machine working cycle
if (_kbhit()){
21
ch=_getch(); // read the character and clear the input buffer
if((ch=='B')||(ch=='b')) x1=1, x2=0, x3=0, x4=0; // remote control button pushed
if((ch=='O')||(ch=='o')) x1=0, x2=1, x3=0, x4=0; // gate fully open
if((ch=='C')||(ch=='c')) x1=0, x2=0, x3=1, x4=0; // gate fully closed
if((ch=='P')||(ch=='p')) x1=0, x2=0, x3=0, x4=1; // someone passing
} else x1=0, x2=0, x3=0, x4=0; // nothing happens
if (S1){
if(x1 && (!x2)){
printf("\nThe gate is OPENING");
S1=0, S2=1;
}
}
if (S2){
if((!x1) && x2){
printf("\nThe gate is STILL AND FULLY OPEN");
timer=WAITCYCLES, S2=0, S3=1;
}
}
if (S3){
if((x1 && (!x4))||((timer==0) && (!x4))){
// timer==0 corresponds to x5=1
printf("\nThe gate is CLOSING");
S3=0, S4=1;
}
if(x4) timer=WAITCYCLES;
else if ((!x1) && (timer!=0)) timer--;
}
if (S4){
if((!x1) && x3){
printf("\nThe gate is STILL AND FULLY CLOSED");
S4=0, S1=1;
}
if(x4){
if (!x3) printf("\nSomeone passing: The gate is STILL but NOT FULLY CLOSED");
else printf("\nSomeone passing: The gate is STILL and FULLY CLOSED");
S4=0, S1=1;
}
}
} // close for cycle
} // close main
4) With your PC simulate a finite state machine able to control the stop and go light of a car
park. The car park can host a maximum of N cars. When a car enters the park a sensor
provides the digital signal x1=1 in output; when no car is entering the park x1=0. When a car
goes out another sensor provides the digital signal x2=1 in output; when no car goes out of
the park x2=0. When the number of cars in the park is N the park light should be red,
otherwise green. When green the number of vacancies should be displayed, too.
-
There are two digital inputs from two sensors: x1 and x2. An internal system variable
cont is used to count the number of cars present in the park; hence N-cont is the number
of vacancies. When cont==N the light should become green. When cont==0 there are no
cars in the park. Again, in our simulation we can handle only one input signal at a time,
but the State Graph of the system is consistent even when input signals are changed
simultaneously. A Mealy State Graph with only two states is sufficient to describe our
system. S1 is the initial state where the number of cars is always less than N. Therefore,
when a car enters and no car goes out there is a transition from S1 to S2 during which
cont is incremented and if it is less than 100 the green light is updated with the number of
vacancies, otherwise the red light is displayed. In state S2 whenever a car enters (and no
22
one goes out) cont is incremented unless it is already equal to N. When a car goes out and
no car enters, there is a transition from S2 to S1 during which cont is decremented, the
light is green and the number of vacancies is updated.
10/cont++, (cont!=N)?N-cont:Red;
S2
S1
01/(cont!=0)?cont--,N-cont:N-cont;
10/if(cont!=N)cont++;
(cont!=N)?N-cont:Red;
01/cont--, N-cont
When inputs (x1,x2)=(0,0) or (x1,x2)=(1,1) no action is performed; this is the reason why
these cases are not shown in the State Graph of the system. Also note some actions are
written using the C instruction (cont!=100)?action1:action2; whose meaning is:
if (cont!=100){
action1;
} else {
action2;
}
Here follows the C code implementing the State Graph:
// car park stop and go light management - carpark.cpp
#include <stdio.h>
#include <conio.h>
#include <iostream>
// number of places available in the park
#define N 20
// program variables definition
int cont=0; // current number of cars in the park
bool x1=0, x2=0, S1=1, S2=0;
char ch;
int main (){
printf("\nMEANINGS OF KEYS:");
printf("\nI or i is the car entering signal"); // x1 input
printf("\nO or o is the car going out signal\n\n"); // x2 input
printf("\nGREEN LIGHT: %d VACANCIES",N-cont);
for(;;){ // finite state machine working cycle
while(_kbhit()){
x1=0, x2=0;
ch=_getch(); // read the character and clear the input buffer
if((ch=='I')||(ch=='i')) x1=1, x2=0; // a car enters
if((ch=='O')||(ch=='o')) x2=1, x1=0; // a car goes out
if (S1){
if(x1 && (!x2)){
cont++, system("cls");
(cont!=N)?printf("\nGREEN LIGHT: %d VACANCIES",N-cont):printf("\nRED LIGHT: NO VACANCIES");
S1=0, S2=1;
}
if((!x1) && x2){
if(cont!=0) cont--;
system("cls");
printf("\nGREEN LIGHT: %d VACANCIES",N-cont);
}
break;
}
if (S2){
if((!x1) && x2){
cont--, system("cls");
printf("\nGREEN LIGHT: %d VACANCIES",N-cont);
S2=0, S1=1;
}
23
if(x1 && (!x2)){
if(cont!=N) cont++;
system("cls");
(cont!=N)?printf("\nGREEN LIGHT: %d VACANCIES",N-cont):printf("\nRED LIGHT: NO VACANCIES");
}
break;
}
} // close while cycle
} // close for cycle
} // close main
Note a while(){…} cycle with break instructions has been used in order to perform a
single state transition for each key pressed. x1=1 corresponds to a single pressure of
the key ‘I’ or ‘i’, while x2=1 to a single pressure of the key ‘O’ or ‘o’.
24
Download