Class equivelant in C?

In simple terms, how can I make this work in C?

I'm working on a robot program for virtual vex pic robots. What I am wondering is, I have a program made for C++ that uses classes. It's finished and fully functional. The class stores information about a particular sensor on a robot, like gyros or rangefinders. If you look up "PID control" you'll know what I'm talking about.

I was wondering how I can make an equivalent of a class in C, or how close I can come. Here's the files. I don't mind if people use it as long as I get some credit.

PID.cpp (to be in C)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#include "PID2.h"

	// Initializes the PID program.
PID_Calc::PID_Calc()
{Reset();}	// Runs the actual reset function.

	// Sets all variables to zero and resets the PID program.
void PID_Calc::Reset()
{
		// Values concerning the P, I, and D values.
	kp = 0;
	ki = 0;
	kd = 0;
	P_Value = 0;
	I_Value = 0;
	D_Value = 0;
	
		// Values concerning distance to the target value and the P term.
	Point_Value = 0;
	Error = 0;
	P_Error = 0;
	Distance = 0;

		// Values concerning the I term.
	I_Sum = 0;
	I_Max = 0;
	
		// Values concerning the D term.
	Current_PV = 0;
	Prevoius_Point_Value = 0;
	Velocity = 0;
	Filter = 0;
	DP_1 = (sizeof (Previous_Velocities) / sizeof (Previous_Velocities[0]));
	DP_2 = 0;
	for (DP_2; DP_2 < DP_1; DP_2++)
	{Previous_Velocities[DP_2] = 0;}
	DP_1 = 0;
	DP_2 = 0;
	DP_3 = 0;
	DP_4 = 0;
	Average_Velocity = 0;
	
		// Values concerning the final output.
	Max_O = 0;
	Output = 0;
	Prev_O = 0;
	Max_A = 0;
	Out_PN = 0;
	
		// Values concerning done cycles.
	Done_Count = 0;
	First_Cycle = true;
}

	// Sets several variables concerning distance to the target and the P value.
void PID_Calc::Calc_Location(double PointValue, double Destination, double Epsilon)
{
	Point_Value = PointValue;	// Current value.
	Current_PV = Point_Value;	// Copies current point value for later use.
	Distance = Point_Value - Destination;	// Finds diatance to target;
	Error = abs(Distance) > Epsilon ? Distance : 0;	// If the distance is less than the epsilon, then the error is zero, otherwise it's the distance.
}

	// Sets P, I, and D multipliers.
void PID_Calc::Set_Multipliers(double K_P, double K_I, double K_D)
{
	kp = K_P;
	ki = K_I;
	kd = K_D;
}

	// Sets varoius limits.
void PID_Calc::Limits(double Max_Output, double Max_Acceleration, double P_Max_Value, int I_Sum_Max, int D_Filter)
{
	Max_O = abs(Max_Output);	// Max output.
	Max_A = abs(Max_Acceleration);	// Max output acceleration.
	P_Full = abs(P_Max_Value);	// Defines the distance at which P is at max.
	I_Max = abs(I_Sum_Max);	// Defines how many cycles the I value will cumulate for until it's full.
	Filter = abs(D_Filter);	// Defines how many prevoius velocities D will use, to filter "noise", or inconsistencies.
}

	// Resets variables concerning the I value.
void PID_Calc::Reset_Sum()
{I_Sum = 0;}	// Resets I term cumulation.

	// Resets variables concerning the D value.
void PID_Calc::Reset_D()
{
	Velocity = 0;
	Prevoius_Point_Value = 0;
	DP_1 = (sizeof (Previous_Velocities) / sizeof (Previous_Velocities[0]));	// Finds the size of the array used for D value.
	DP_2 = 0;
	for (DP_2; DP_2 < DP_1; DP_2++)	// Sets the prevoius velocities of D to zero.
	{Previous_Velocities[DP_2] = 0;}
	DP_1 = 0;
	DP_2 = 0;
	DP_3 = 0;
	DP_4 = 0;
	First_Cycle = true;
}

	// Calculates the final output.
double PID_Calc::Calc_PID()
{
		// Keeps the P value within it's defined limits.
	if (Error > P_Full)
	{P_Error = P_Full;}
	else if (Error < -P_Full)
	{P_Error = -P_Full;}
	else
	{P_Error = Error;}
	
		// Calculate P value. Takes into account the max P value to make calibration easier.
	if (P_Full == 0)
	{P_Value = 0;}
	else
	{P_Value = -1 * kp * P_Error / P_Full;}
	
		// Increase or decrease the I value every cycle, depending on the circumstances.
	if (Error > 0)	// Error is below zero. Keep subracting until the sum is at it's minimum (opposite of the max).
	{
		if (I_Sum > (-I_Max))
		{I_Sum--;}
		else
		{I_Sum = -I_Max;}
	}
	else if (Error < 0)	// Error is above zero. Keep adding until it's at its maximum.
	{
		if (I_Sum < I_Max)
		{I_Sum++;}
		else
		{I_Sum = I_Max;}
	}
	else	// Error is zero, or within epsilon. Keep adding or sumtracting until it is at zero.
	{
		if (I_Sum > 0)
		{I_Sum--;}
		else if (I_Sum < 0)
		{I_Sum++;}
		else
		{I_Sum = 0;}
	}
	
		// Calculate I value. It takes into account the max I value to make calibration easier.
	if (I_Max == 0)
	{I_Value = 0}
	else
	{I_Value = ki * I_Sum / I_Max;}
	
		// Finds current velocity.
	if (First_Cycle)	// Sets initial velocity to zero, since there was no known previous location.
	{
		Velocity = 0;
		First_Cycle = false;
	}
	else	// Calculates velocity by subrtacting the last known value from the current value.
	{Velocity = Current_PV - Prevoius_Point_Value;}
	DP_1 = (sizeof (Previous_Velocities) / sizeof (Previous_Velocities[0]));	// Gets the size of the array to be used for the D value.
	DP_2 = DP_1 - 1;	// Makes a copy of DP_1 for later use.
	
		// Moves all the prevoius velocities "down the line".
	for (DP_2; DP_2 > 0; DP_2--)
	{Previous_Velocities[DP_2] = Previous_Velocities[DP_2 - 1];}
	Previous_Velocities[DP_2] = Velocity;	// Makes the first value of the array the current velocity.
	
		// Makes sure the filter size is within the array limits.
	if (Filter > DP_1)
	{Filter = DP_1;}
	else if (Filter < 1)
	{Filter = 1;}
	DP_2 = Filter;	// Reycles DP_2 for later use.
	DP_3 = DP_2 - 1;	// Uses DP_2 to make DP_3.
	DP_4 = 0;	// Initializes the total of the array being used to zero.

		// Adds up the total of the array values. The number of elements used is determined by the filter.
	for (DP_3; DP_3 >= 0; DP_3--)
	{DP_4 += Previous_Velocities[DP_3];}
	Average_Velocity = DP_4 / DP_2;	// Finds the average value of the prevoius velocities based on the set filter.
	
		// Calculates the D value based on the average of the prevoius velocities.
	D_Value = kd * Average_Velocity;
	
		// Saves the current location for the next cycle.
	Prevoius_Point_Value = Current_PV;
	
		// Calculates final output and keeps it within limits.
	Output = P_Value + I_Value - D_Value;	// Calculates the initial output.
	if (Max_A != 0)	// Limits the acceleration of the output ecxept if it's zero or less.
	{
		if (Output - Prev_O >= Max_A)
		{Output = Prev_O + Max_A;}
		else if (Prev_O - Output >= Max_A)
		{Output = Prev_O - Max_A;}
	}
	if (Max_O != 0) // Limits final output unless it's zero or less.
	{
		Out_PN = Output >= 0 ? 1 : -1;
		Output = abs(Output) >= Max_O ? Max_O * Out_PN : Output;
	}
	Prev_O = Output;	// Saves current output for the next cycle.
	return Output;
}

	// Tests whether or not the location has been within the desired range for the desired number of cycles.
bool PID_Calc::Is_Done(int Min_Done_Cycles)
{
	if (Error == 0)
	{Done_Count++;}
	else
	{Done_Count = 0;}
	
	if (Done_Count >= Min_Done_Cycles)
	{return true;}
	else
	{return false;}
}

PID.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#ifndef PID_h_
#define PID_h_
#include <math.h>
class PID_Calc
{
	public:
		PID_Calc();
		void Reset();
		void Reset_Sum();
		void Reset_D();
		void Calc_Location(double PointValue, double Destination, double Epsilon);
		void Set_Multipliers(double K_P, double K_I, double K_D);
		void Limits(double Max_Output, double Max_Acceleration, double P_Max_Value, int I_Sum_Max, int D_Filter);
		double Calc_PID();
		bool Is_Done(int Min_Done_Cycles);
	private:

/* Each sensor uses these variables. They need to be plugged into the functions above. Each function is used separately in the main program.*/

	// All variables are explained in the Reset function.
		double kp, ki, kd, P_Value, I_Value, D_Value;
		double P_Full;
		int I_Max, I_Sum, Filter;
		double Error, P_Error, Distance;
		double Point_Value, Current_PV, Prevoius_Point_Value, Velocity;
		double Previous_Velocities[10];
		int DP_1, DP_2, DP_3;
		double DP_4;
		double Average_Velocity;
		double Max_O, Output, Prev_O, Max_A;
		int Out_PN;
		int Done_Count;
		bool First_Cycle;
};
#endif 

Structures are what you're looking for. They don't have any functions attached to them though, just the data values. Furthermore, the data values in structures aren't considered private, so you can access them without going through the functions.

Essentially, you'll need to add the type of class you've made to the parameters of your functions, and separate the functions from the classes, and then you'll have your c version.

Here's an example of what struct and main would look like for you.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct PID_Calc
{
	double kp, ki, kd, P_Value, I_Value, D_Value;
	double P_Full;
	int I_Max, I_Sum, Filter;
	double Error, P_Error, Distance;
	double Point_Value, Current_PV, Prevoius_Point_Value, Velocity;
	double Previous_Velocities[10];
	int DP_1, DP_2, DP_3;
	double DP_4;
	double Average_Velocity;
	double Max_O, Output, Prev_O, Max_A;
	int Out_PN;
	int Done_Count;
	bool First_Cycle;
};


1
2
3
4
5
6
int main()
{
	PID_Calc a;
	a.kp=5.0; // This will work, so that's an example of how it's not private
	Reset(a); // Will modify your struct of a to however the reset function works. You'll simply need to add "a." before each of the variables in the reset() function.
}



More on structures here: http://cprogramminglanguage.net/c-structure.aspx
Last edited on
Will this allow me to use different types interchangeably? Like:

1
2
3
4
struct PID_Calc
{
	double kp; // Simplified the header just for talking purposes.
};


1
2
3
4
5
6
7
8
9
10
11
12
Reset(char type[])
{
   type.kp = 0; // "type" is a variable based on the parameters.
}

int main()
{
	PID_Calc a;
        PID_Calc b;
	Reset("a");
        Reset("b");
}

Would this allow me to specify which type to use without rewriting the function, like having the type be a variable?
Last edited on
Types and variable names are two different things.

You can have a function take any object. Just pass it by pointer (since this is C)

example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// assuming PID_Calc is typedef'd:
typedef struct
{
  double kp;
} PID_Calc;

// a "member function"
void PID_Calc_Reset(PID_Calc* obj)
{
   obj->kp = 0;
}

// how you use it:
int main()
{
  PID_Calc a;
  PID_Calc b;

  PID_Cacl_Reset(&a);
  PID_Cacl_Reset(&b);
}
Thanks for the help, I really appreciate it!
Topic archived. No new replies allowed.