|
You should give your actual Analogue-Value to the Method from OG (as it's parameter) and you will get the Average-Value as Result back from this method.
The Variables 'Count' and 'Samples' and 'Index' must be declared outside this method ...
|
|
|
|
|
Nice, but how about ditching the loop? Just add the newValue to the sum (as a member, not local) each time, then deduct the number which it replaces in the array and you've just reduced the algorithmic complexity.
I know, I'm just in one of those picky moods today.
Regards,
Rob Philpott.
|
|
|
|
|
Yeah, but he seems to be a beginner, so I thought I'd make it as obvious as possible.
I'd probably make it a MovingAverage<T> class in my code, and complicate it that way!
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Yes I am a beginner
Now I made some slight changes and I am getting average of the last 5 numbers, but still my problems is not solved
When I generate my Sensor values in the text box , they appear like this:
TimeStamp AI.1 AI.2 AI.3 AI.4 AI.5 AI.6 AI.7
13:29:05.091 0.726 0.249 0.771 0.294 0.816 0.338 0.861
13:30:40.325 0.817 0.111 0.404 0.698 0.991 0.284 0.962
13:31:19.709 0.768 0.467 0.166 0.865 0.564 0.263 0.915
13:32:00.943 0.558 0.772 0.985 0.198 0.412 0.625 0.594
13:32:31.391 0.206 0.658 0.109 0.560 0.012 0.463 0.720
What happens is that program calculates average of the last five numbers in the text box which means in this case Moving Average=(0.109+0.560+0.012+0.463+0.720)/5=0.373.
While the moving average for example for AI.1 would be:(0.726+0.817+0.758+0.558+0.206)/5=0.615
This is obviously Wrong as I am getting average of the values from different sensors.
I need average of the last five values for Sensor AI.1, AI2,....etc
So I need to make some more changes to get this done.
Here is the code I have changed:
No changes to your method:
private double GetMovingAverage(double newVal)
{
if (count != samples.Length) count++;
samples[index++] = newVal;
if (index == samples.Length) index = 0;
double sum = 0.0;
for (int i = 0; i < count; i++)
{
sum += samples[i];
}
return sum / (double)(count);
}
The method was implemented like this:
for (int id = 0; id < maxAI; id++)
{
double AnalogSensorValues = sObj[id].GetAnalogValue();
sTxt = AnalogSensorValues.ToString("F3");
textSensorValues.Text += sTxt + " ";
txtFilterValues.Text = fTx1;
txtFilterValues.Text = GetMovingAverage(AnalogSensorValues).ToString("F3");
}
modified 15-Feb-18 7:44am.
|
|
|
|
|
If you want separate moving averages, then you want to set up separate values stores - the array in my code is what you need for a single sensor, for t=five sensors you need five arrays.
The way I would do it is to set up a MovingAverage class, with its own count, index, and array, and move the method to that.
Then in your "main code" you create five instances of the MovingAverage class and use one for each sensor.
Does that make sense? (Remember, I don't know what "level" of beginner you are! )
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
I am a complete beginner, started with C# like days ago.What you are saying makes sense , but another thing is how to do it?
What confuses me generally are arrays, I have noticed I find working with those challenging right now.
|
|
|
|
|
If you are having difficulty with arrays, then you probably need to shelve this project for a while - a week or two maybe - and focus on learning the basics before you continue to work on it.
Classes, arrays, and so forth are the "Building blocks" of the language, and you really do need to understand them properly before you get much beyond "Hello world!"
If you don't, then you will end up with something that works - probably - but you have no real idea of how or why, and that means that it becomes unmaintainable which is very bad news!
I can't teach you classes - there is far too much for a little text box like this, and I can't see when your eyes start to glaze over - and the only other way to do this is to have arrays of arrays, and several arrays of integers, which gets extremely clumsy and hard to read very, very quickly.
Seriously: go back to your book - or your course - and follow it through. trying to jump ahead to "the interesting stuff" just leaves huge holes in your knowledge that makes more complicated things very difficult to work out.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
There is also another way to build this Average without using an array - but the base-problem is the same : it is necessary to know what the code does and how it works.
Independant from this - here is another Code-Solution :
int CountAct = 0;
double ValueLast = 0;
public double getAverage(double actValue, int Smoothing)
{
if (Smoothing < 1)
Smoothing = 1;
CountAct = Math.Min(CountAct + 1, Smoothing);
double myDivider = Math.Max((double)CountAct - 1, 0);
double Result = (ValueLast * myDivider + actValue) / (myDivider + 1);
ValueLast = Result;
return Result;
}
Here you can build an average from as much values you like - you only have to tell the method the smoothing-value you like to have ...
modified 16-Feb-18 6:33am.
|
|
|
|
|
Thanks for the reply. Don't see exactly how this code snippet is gonna solve my exact problem, but thanks anyways .
Original Griff's code for getting the moving average of the last five values works , but problem is as I mentioned that I am getting moving average of last 5 values displayed on the screen and they are from different sensors so that obviously gives undesirable result.
I need to make some sort of array to store my past values for the respective sensors and than apply the GetMovingAverage() on each of the stored arrays.
The thing is I don't know how to do that
|
|
|
|
|
Alternatively, you could have a dictionary of moving average objects; one for each sensor. It shouldn't be too hard for you to modify Griff's code to do this (hint, each sensor would be a key in the dictionary).
This space for rent
|
|
|
|
|
The difference between my Solution and the Solution from OG is that my Solution don't need an Array for building a single Average. Another difference is that the smoothing is variable.
If you want to build also an average from the average of some sensors you need to add the values of the sensor-averages and divide it throught the count of the sensors which are added.
But that was allready mentioned by OG ... where do you stuck there ?
SumAverage := (Sensor_1_Average + Sensor_2_Average + Sensor_3_Average + Sensor_4_Average + Sensor_5_Average) / 5.0 ;
|
|
|
|
|
Hi, OG's Moving Average works fine. The problem is I am not able to apply it to the proper values.
It should be simple thing to fix, but its not.
As I said in one of the posts above. I get the Moving Average, means I get the average of the last 5 numbers , but I need to get it for each sensor column not row wise as I am currently. See the explanation below.
Ho do I fix that ??
TimeStamp AI.1 AI.2 AI.3 AI.4 AI.5 AI.6 AI.7
13:29:05.091 0.726 0.249 0.771 0.294 0.816 0.338 0.861
13:30:40.325 0.817 0.111 0.404 0.698 0.991 0.284 0.962
13:31:19.709 0.768 0.467 0.166 0.865 0.564 0.263 0.915
13:32:00.943 0.558 0.772 0.985 0.198 0.412 0.625 0.594
13:32:31.391 0.206 0.658 0.109 0.560 0.012 0.463 0.720
What happens is that program calculates average of the last five numbers in the text box which means in this case Moving Average=(0.109+0.560+0.012+0.463+0.720)/5=0.373.
While the moving average for example for AI.1 would be:(0.726+0.817+0.758+0.558+0.206)/5=0.615
This is obviously Wrong as I am getting average of the values from different sensors.
I need average of the last five values for Sensor AI.1, AI2,....etc
So I need to make some more changes to get this done.
|
|
|
|
|
You should read carefully what is suggested to you ...
I would say : all what you need is told to you ...
|
|
|
|
|
A new day and I will try it once more to explain.
The fact is : you don't understand what you got suggested.
The Solution from OG and also the Solution from me builds the "moving Average" from the incoming values from ONE sensor - not from one timestanp.
So - what both Solutions are doing is : take the raw-value of one sensor (that means the incoming actual value) and build an average with the saved/stored values from the same sensor - that means the values before). By this you get a kind of smoothing of the incoming values. The return-value (the result) of the posted method should be stored by you in your table.
I suppose that you completly misunderstood the made Suggestions.
If you now still don't know how to get further you should post your actual method which writes the incoming values in your table (or List or whatever). Perhaps then we could explain you the mistake you make ...
|
|
|
|
|
Hi again Ralf and thanks for the patience. I understood what OG's method does in terms of the result I got. My problem is now how do I make this list to store my numbers that get generated by the GetAnalog Method.
I would need to have historical analog values for each Sensor and then apply OG's method on each sensor array or list , whatever is made.
Here is the GetAnalogMethod() from the Sensor.cs class,
public double GetAnalogValue(double minAnalogVolt=0.00F,double maxAnalogVolt=1.00F)
{
if(minAnalogVolt <= AnalogVal && AnalogVal<= maxAnalogVolt)
AnalogVal = rSensVal.NextDouble();
return AnalogVal;
}
In here I call this GetAnalogValue() method to generate "random" analog values for each of my sensor IDs:
for (int id = 0; id < maxAI; id++)
{
double AnalogSensorValues = sObj[id].GetAnalogValue();
sTxt = AnalogSensorValues.ToString("F3");
textSensorValues.Text += sTxt + " ";
txtFilterValues.Text = fTx1;
txtFilterValues.Text = GetMovingAverage(AnalogSensorValues).ToString("F3");
}
If you see above I store all my sensor ID values in a double variable I call AnalogSensorValues.I should have ideally have had a list so that each value gets stored and can be accessed so that GetMoving Average() method can be applied to it.
So yes, that is my problem. Basically sorting these values in a List. This may sound silly, but I don't know how to do it
modified 17-Feb-18 6:42am.
|
|
|
|
|
I'm not sure if I got you right ...
Basicly your 2nd code-snippet looks good. But ... what do you want to see in your Textbox 'txtFilterValues' ?
Inside your loop you are overwriting each stored text with the new one ...
My 2nd problem with your code is :
You are using the method 'GetMovingAverage' completely wrong, because it builds it's average (as I described before) from it's own stored values (that you used for the method before).
So your loop must be complete different (and in my opinion the posted method doesn't really solve your problem).
Try as code something like this :
double AnalogSensorAverage
for (int id = 0; id < maxAI; id++)
{
double AnalogSensorValues = sObj[id].GetAnalogValue();
AnalogSensorAverage = GetMovingAverage(AnalogSensorValues);
sTxt = AnalogSensorValues.ToString("F3");
textSensorValues.Text += sTxt + " ";
}
txtFilterValues.Text = AnalogSensorAverage.ToString("F3");
|
|
|
|
|
|
OK ... in your code snippet (before my last response) I haven't seen (or realized) that your loop works with the Analogue Inputs - not the different values from each Input (in that case my posted Solution has matched).
So you have another loop (outside the AI-Loop) which takes the samples ...?
I will create another code-suggestion for you which works with an array you need. But now (and that is what you really wanted to have) you would get a method which builds an Average at the end of the meassurering - not moving. The difference is : Moving means during and not at the End ...
One moment please ...
|
|
|
|
|
double[,] AnalogSensorValue = new double[7,6];
for (int cycle = 1; cycle <= 5; cycle++)
{
for (int sensor = 0; sensor <= 6; sensor++)
{
AnalogSensorValue[sensor, cycle] = 1234;
}
}
for (int sensor = 0; sensor <= 6; sensor++)
{
AnalogSensorValue[sensor, 0] = 0;
for (int cycle = 1; cycle <= 5; cycle++)
{
AnalogSensorValue[sensor, 0] += AnalogSensorValue[sensor, cycle];
}
AnalogSensorValue[sensor, 0] /= 5;
}
programming in C# is not my favourite ...
|
|
|
|
|
Thanks a lot.Have some questions. Trying to understand your code.
You are running 5 cycles to save 5 sensor values?
What is the measuring value you are referring to?
Are you talking about:
Analogsensorvalues=sObj[id].GetDigitalValue();
Because this is where and how I actually enter or get the randomly generated sensor values from the Sensor.cs class.
for (int cycle = 1; cycle <= 5; cycle++)
{
for (int sensor = 0; sensor <= 6; sensor++)
{
AnalogSensorValue[sensor, cycle] = AnalogSensorValues;
txtNumberWritings.Text += sensor.ToString();
}
}
I tired something here, but not really sure if I understood how I am suppose to store my values.
I am just getting 0,1,2,3,4,5,6 in the text box
If the code works as intended I should be getting values I have for each sensor?
|
|
|
|
|
Yes ... I'm talking about that assignment
Analogsensorvalues=sObj[id].GetDigitalValue();
This couldn't be tested by me. But you have to realize that in my "method" the variable 'id' isn't used. I guess you you replace it by my loop-variable 'sensor'.
Inside the loop which you refer you assign 'sensor.toString' to the Textbox. That isn't correct - here you have to assign 'AnalogSensorValues.toString' as you have done before.
If you make all changes to my code-snippet as necessary the code should work as you want ...
If not please provide your actual code - apologize that my code-suggestion wasn't better ... but because C# isn't my 1st choice for coding I had to test my code before providing - so some of your assignments couldn't be made by me because I didn't have your complete project ...
Waiting for your Feedback ...
|
|
|
|
|
Hi Ralf, I think I have solved the problem now.
I created a class for MovingAverageFilter.
The only little problem I have left is implementing an automatic sampling by ticking off check box. Struggling with that at the moment.
Also I guess I do not have proper Random numbers as I always start off from the same Seed No., would be great to Randomize that properly, so that it creates completely new numbers next time I run the app.
See my code:
Main Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Xml.Linq;
using System.Windows.Forms;
using System.Globalization;
using System.Linq;
using System.Collections.Generic;
using System.Collections;
namespace DAQ_Simulator
{
public partial class Form1 : Form
{
int counter;
int count_lines;
System.Timers.Timer t;
int LoggingTime=56;
int LogTimeLeft;
float SamplingTime = 5.9f;
private int clickcounterSampling;
private DateTime datetime;
private DateTime datetime2;
private DateTime NextLoggingTime;
int maxAI = 7;
int maxDI = 3;
int maxSid = 9;
String SensorText;
String sTxt1;
String fTxt;
Sensor[] SensorObject = new Sensor[10];
MA_Filter[] FilterObject = new MA_Filter[10];
public Form1()
{
InitializeComponent();
for (counter = 0; counter < maxSid; counter++)
{
SensorObject[counter] = new Sensor(counter);
FilterObject[counter] = new MA_Filter(counter);
}
}
private void displaySensorData(object sender, EventArgs e)
{
}
private void groupSampl_Enter(object sender, EventArgs e)
{
}
private void textSampling_TextChanged(object sender, EventArgs e)
{
}
private void btnSampling_Click(object sender, EventArgs e)
{
txtNextSamplingTime.Text = "5.9sec";
clickcounterSampling++;
if (clickcounterSampling == 1)
{
txtSensorValues.Text = "TimeStamp AI.1, AI.2, AI.3, AI.4, AI.5, AI.6, AI.7, DI.1, DI.2, DI.3" + "\r\n";
txtFilterValues.Text = "AI.1, AI.2, AI.3, AI.4, AI.5, AI.6, AI.7" ;
}
if (clickcounterSampling >= 5)
{ txtFilterValues.Text += "\r\n"; }
if (SamplingTime <= 0)
{
SamplingTime = SamplingTime + 5.9f;
}
if (SamplingTime <= 5.9f && SamplingTime >= 0)
{
btnSampling.Enabled = false;
}
timer1 = new System.Windows.Forms.Timer();
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Interval = 1000;
timer1.Start();
datetime2 = DateTime.Now.AddSeconds(5).AddMilliseconds(900);
String time2 = datetime2.ToString("HH:mm:ss.FFF", CultureInfo.InvariantCulture);
textSampling.Text = time2;
count_lines = txtFilterValues.Lines.Length;
count_lines =count_lines- 1;
txtNumberWritings.Text = count_lines.ToString();
Sampling();
}
private void groupLogg_Enter(object sender, EventArgs e)
{
}
private void labelLoggingText_Click(object sender, EventArgs e)
{
}
private void btnLogging_Click(object sender, EventArgs e)
{
if (LoggingTime == 0)
{
LoggingTime = LoggingTime + 56;
}
if (LoggingTime <= 56 && LoggingTime >= 0)
{
btnLogging.Enabled = false;
}
timer2 = new System.Windows.Forms.Timer();
timer2.Tick += new EventHandler(timer2_Tick);
timer2.Interval = 1000;
timer2.Start();
txtLogging.Text = LoggingTime.ToString();
NextLoggingTime = DateTime.Now.AddSeconds(4);
String time3 = NextLoggingTime.ToString("HH:mm:ss.FFF", CultureInfo.InvariantCulture);
txtLogging.Text =time3;
Logging();
}
private void timer1_Tick(object sender, EventArgs e)
{
SamplingTime--;
txtNextSamplingTime.Text = SamplingTime.ToString("F2")+"sec";
if (SamplingTime <= 0)
{
timer1.Stop();
btnSampling.Enabled = true;
txtNextSamplingTime.Clear();
}
}
private void Sampling()
{
datetime = DateTime.Now;
txtSensorValues.Text += datetime.ToString("HH:mm:ss.FFF", CultureInfo.InvariantCulture) + " ";
double[] AnalogSensorValues = new double[maxAI];
for (int id = 0; id < maxAI; id++)
{
AnalogSensorValues[id] = SensorObject[id].GetAnalogValue();
double filter=+FilterObject[id].CalculateMovingAverage(AnalogSensorValues[id]);
SensorText = AnalogSensorValues[id].ToString("F3");
fTxt = filter.ToString("F3");
txtSensorValues.Text += SensorText + " ";
if (clickcounterSampling >= 5)
{
txtFilterValues.Text += " " + fTxt + " ";
}
}
for (int id = maxSid - maxDI; id < maxSid; id++)
{
int DigitalSensorValues = SensorObject[id].GetDigitalValue();
sTxt1 = DigitalSensorValues.ToString();
txtSensorValues.Text += " " + sTxt1 + " ";
}
txtSensorValues.Text += "\r\n";
}
private void helpToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("This is a DAQ Simulator that recieves 7analog and 3 digital signals and displayes the values in a textbox." +
"Raw measuremnt data is filtered with a Moving Average filter where average of the 5 last measuremnts are displayed","Input Information",System.Windows.Forms.MessageBoxButtons.OK);
}
private void Logging()
{
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
using (Stream s = File.Open(saveFileDialog1.FileName,FileMode.CreateNew))
using (StreamWriter sw = new StreamWriter(s))
{
sw.WriteLine(txtSensorValues.Text+txtSensorValues);
textFilePath.Text = string.Format("{0}", openFileDialog1.FileName);
}
textFilePath.Text = saveFileDialog1.FileName.ToString();
}
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void folderBrowserDialog1_HelpRequest(object sender, EventArgs e)
{
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
}
private void timer2_Tick(object sender, EventArgs e)
{
LoggingTime--;
txtNextLogg.Text = LoggingTime.ToString()+"sec";
if (LoggingTime <= 0)
{
timer2.Stop();
btnLogging.Enabled = true;
txtNextLogg.Clear();
txtNextLogg.Text = "56 sec";
}
}
private void chckAutSampl_CheckedChanged(object sender, EventArgs e)
{
if (chckAutSampl.Enabled==true)
{
SamplingTime--;
txtNextSamplingTime.Text = SamplingTime.ToString("F2") + "sec";
if (SamplingTime <= 0)
{
timer3.Stop();
txtNextSamplingTime.Clear();
}
timer3 = new System.Windows.Forms.Timer();
timer3.Tick += new EventHandler(timer1_Tick);
timer3.Interval = 1000;
timer3.Start();
}
}
private void timer3_Tick(object sender, EventArgs e)
{
SamplingTime--;
txtNextSamplingTime.Text = SamplingTime.ToString("F2") + "sec";
if (SamplingTime <= 0)
{
timer3.Stop();
chckAutSampl.Enabled = false;
txtNextSamplingTime.Clear();
}
}
}
}
Sensor class,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DAQ_Simulator
{
public class Sensor
{
double AnalogVal;
int DigVal;
int sId;
Random rSensVal;
public Sensor(int id)
{
sId = id;
rSensVal = new Random(id);
AnalogVal = 0.0F;
}
public double GetAnalogValue(double minAnalogVolt=0.00F,double maxAnalogVolt=1.00F)
{
if(minAnalogVolt <= AnalogVal && AnalogVal<= maxAnalogVolt)
AnalogVal = rSensVal.NextDouble();
return AnalogVal;
}
public int GetDigitalValue(int digMin = 0, int digMax = 1)
{
DigVal = rSensVal.Next(0,2);
return DigVal;
}
public int GetSensId()
{
return sId;
}
}
}
Moving Average Filter Class,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DAQ_Simulator
{
public class MA_Filter
{
public double[] samples = new double[5];
public int index;
public int count;
public MA_Filter(int id)
{
count = 0;
index = 0;
}
public double CalculateMovingAverage(double newVal)
{
if (count != samples.Length) count++;
samples[index++] = newVal;
if (index == samples.Length) index = 0;
double sum = 0.0;
for (int i = 0; i < count; i++)
{
sum += samples[i];
}
return sum / (double)(count);
}
}
}
|
|
|
|
|
Well ... very good if it works now.
What I like to know now : for what is that good ? OK ... you have a kind of Simulation ... but what is the final goal ? Do you want to communicate with a PLC which will give you Values of a Meassurement ? In that case you should realize that a PLC (or something similar) will give you each Millisecond a new Value ... and those Values (of course) need to be smoothed - but continiuesly. Or is the goal something complete different ?
|
|
|
|
|
I just did an assignment for a specific course I am doing at the Uni.This was just meant as a simulation so there really is no further goal behind this. Sensors were suppose to simulate signals from field instruments I guess.
Thanks for the help Ralf Maier.
|
|
|
|
|
Into the deep...
double[] values = new double[] { 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 2.0 };
double avg = ( values.Length > 0 ) ? values.Reverse().Take( 5 ).Average() : 0;
Console.WriteLine( $"Avg: {avg}" );
"(I) am amazed to see myself here rather than there ... now rather than then".
― Blaise Pascal
|
|
|
|
|