I'm working on a new program in C# WPF, and part of it includes averaging ten PingReply.RoundTripTime and dividing it by two to find the one-way trip time. I put all the PingReply instances in an array, arrayReply, and now want to perform the above operations. I've tried a bunch of things, but this is what I have right now:
//New integer replyCount for counting ping replies
int replyCount = 0;
long Atime = 0;
long time = 0;
//for each PingReply instance in arrayReply
foreach (PingReply r in arrayReply)
{
//set long integer (64-bit) Atime (time of ping) to the previous value of Atime
//plus half of the next RoundTripTime divided by 2 (for approx one-way distance)
long oneWayTime = r.RoundtripTime / 2;
long x = Atime + oneWayTime;
//add one to replyCount
replyCount++;
//Divide Atime by number of replies
time = x / replyCount;
}
pingAverage.Text = time.ToString();
However, this results in 0 every time. How can I fix this?
Use LINQ to average:
var time = arrayReply.Average(pr => pr.RoundtripTime) / 2;
Related
The project is based on Eye Tracker. Let me brief the idea behind the project to understand my problem better.
I have the hardware of Tobii C eye tracker. This eye tracker will be able to give out coordinates of the X, Y of where I am looking at. But this device is very sensitive. When I look at 1 point, the eye tracker will send out many different data of coordinates but within ± 100 range which I found out. Even though you are staring at 1 point, your eyes keep moving, therefore giving out many data. This many data (float numbers) are then saved in a text file. Now I only need 1 data (X coordinate) which signifies the 1 point I am staring instead of the many data which are within the ± 100 range and move it to a new text file.
I have no idea how I should code to do that.
These are the float numbers in the text file.
200
201
198
202
250
278
310
315
360
389
500
568
579
590
When I stare at point 1, the data are 200-300, which are within the ± 100 range. I wanna set the 200 as reference point subtracts itself with the next number and check if the resultant value within 100, if it is, remove them. The reference point should keep doing that to the following numbers until it reaches outside the ± 100 range. Once outside the 100 range, now the number is 310, then now this number is the next reference point and do the same and subtract with the following numbers below and check if the resultant value within 100. Once outside the 100 range, the next number is 500, now, that is the new reference point, and do the same. That is my objective. To put it to simpler terms, The reference points should be moved into a new file.
This is my code so far which get the gaze coordinates and stores them in a text file.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Tobii.Interaction;
namespace ConsoleApp1
{
class Program
{
private static void programintro()
{
Console.WriteLine("Press Any Keys To Start");
}
public static void Main(string[] args)
{
programintro();
Console.ReadKey();
double currentX = 0.0;
double currentY = 0.0;
double timeStampCurrent = 0.0;
double diffX = 0.0;
double diffY = 0.0;
int counter = 0;
var host = new Host();
host.EnableConnection();
var gazePointDataStream = host.Streams.CreateGazePointDataStream();
gazePointDataStream.GazePoint((gazePointX, gazePointY, timestamp) =>
{
diffX = gazePointX - currentX;
diffY = gazePointY - currentY;
currentX = gazePointX;
currentY = gazePointY;
timeStampCurrent = timestamp;
if (diffX > 100 || diffX <= -100 || diffY >= 100 || diffY <= -100)
{
counter++;
using (StreamWriter writer = new StreamWriter("C: \\Users\\Student\\Desktop\\FYP 2019\\ConsoleApp1\\ConsoleApp1\\Data\\TextFile1.txt", true))
{
writer.WriteLine("Recorded Data " + counter + "\n=================================================================================================================\nX: {0} Y:{1}\nData collected at {2}", currentX, currentY, timeStampCurrent);
writer.WriteLine("=================================================================================================================");
}
Console.WriteLine("Recorded Data " + counter + "\n=================================================================================================================\nX: {0} Y:{1}\nData collected at {2}", currentX, currentY, timeStampCurrent);
Console.WriteLine("=================================================================================================================");
}
});
//host.DisableConnection();
while (true)
{
if (counter < 10)
{
continue;
}
else
{
Environment.Exit(0);
}
}
Now my Question is how do I code to read the text file and set a
reference number and subtracts itself with the next number and check
if the resultant value within 100 and have a new reference number if
it outside the ± 100 range. Those reference numbers are then stored in
a new text file.
If there is a code example, I will create a new programme and store there and test it out first.
Assuming that the initial data is present in a list, the logic to get all reference points is as follows:
var initialData = new List<float> { 200,201,198,202,250,278,310,315,360,389,500,568,579,590 };
var lstReferencePoints = new List<float>();
var referencePoint = default(float);
foreach(var num in initialData)
{
if(referencePoint == default(float))
{
referencePoint = num;
}
if(Math.Abs(referencePoint - num) > 100)
{
lstReferencePoints.Add(referencePoint);
referencePoint = num;
}
}
lstReferencePoints.Add(referencePoint);
lstReferencePoints contains the list of referencePoints.
Edit: reading the float numbers from a text file to a List
var pointsArray = File.ReadAllLines(your_file_path);
var initialData = new List<float>(pointsArray.Select(float.Parse));
Storing the lstReferencePoints to a new text file:
using(TextWriter tw = new StreamWriter("newFile_Path"))
{
foreach (var item in lstReferencePoints)
tw.WriteLine(item);
}
The project is based on Eye Tracker. Let me brief the idea behind the project to understand my problem better.
I have the hardware of Tobii C eye tracker. This eye tracker will be able to give out coordinates of the X, Y of where I am looking at. But this device is very sensitive. When I look at 1 point, the eye tracker will send out many different data of coordinates but within ± 100 range which I found out. Even though you are staring at 1 point, your eyes keep moving, therefore giving out many data. This many data (float numbers) are then saved in a text file. Now I only need 1 data (X coordinate) which signifies the 1 point I am staring instead of the many data which are within the ± 100 range and move it to a new text file.
I have no idea how I should code to do that.
These are the float numbers in the text file.
200
201
198
202
250
278
310
315
360
389
500
568
579
590
When I stare at point 1, the data are 200-300, which are within the ± 100 range. I wanna set the 200 as reference point subtracts itself with the next number and check if the resultant value within 100, if it is, remove them. The reference point should keep doing that to the following numbers until it reaches outside the ± 100 range. Once outside the 100 range, now the number is 310, then now this number is the next reference point and do the same and subtract with the following numbers below and check if the resultant value within 100. Once outside the 100 range, the next number is 500, now, that is the new reference point, and do the same. That is my objective. To put it to simpler terms, The reference points should be moved into a new file.
This is my code so far which get the gaze coordinates and stores them in a text file.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Tobii.Interaction;
namespace ConsoleApp1
{
class Program
{
private static void programintro()
{
Console.WriteLine("Press Any Keys To Start");
}
public static void Main(string[] args)
{
programintro();
Console.ReadKey();
double currentX = 0.0;
double currentY = 0.0;
double timeStampCurrent = 0.0;
double diffX = 0.0;
double diffY = 0.0;
int counter = 0;
var host = new Host();
host.EnableConnection();
var gazePointDataStream = host.Streams.CreateGazePointDataStream();
gazePointDataStream.GazePoint((gazePointX, gazePointY, timestamp) =>
{
diffX = gazePointX - currentX;
diffY = gazePointY - currentY;
currentX = gazePointX;
currentY = gazePointY;
timeStampCurrent = timestamp;
if (diffX > 100 || diffX <= -100 || diffY >= 100 || diffY <= -100)
{
counter++;
using (StreamWriter writer = new StreamWriter("C: \\Users\\Student\\Desktop\\FYP 2019\\ConsoleApp1\\ConsoleApp1\\Data\\TextFile1.txt", true))
{
writer.WriteLine("Recorded Data " + counter + "\n=================================================================================================================\nX: {0} Y:{1}\nData collected at {2}", currentX, currentY, timeStampCurrent);
writer.WriteLine("=================================================================================================================");
}
Console.WriteLine("Recorded Data " + counter + "\n=================================================================================================================\nX: {0} Y:{1}\nData collected at {2}", currentX, currentY, timeStampCurrent);
Console.WriteLine("=================================================================================================================");
}
});
//host.DisableConnection();
while (true)
{
if (counter < 10)
{
continue;
}
else
{
Environment.Exit(0);
}
}
Now my Question is how do I code to read the text file and set a
reference number and subtracts itself with the next number and check
if the resultant value within 100 and have a new reference number if
it outside the ± 100 range. Those reference numbers are then stored in
a new text file.
Based on your sample data here is the code to get only numbers which has 100+ difference.
static void Main(string[] args)
{
string filename = #"C:\PowershellScripts\test.txt"; // INPUT FILE
String outputFile = #"C:\PowershellScripts\result.txt"; // OUTPUT FILE
string[] data = File.ReadAllLines(filename); // READING FORM FILE
int TotalLine = data.Length; // COUNT TOTAL NO OF ROWS
List<string> FinalList = new List<string>(); // INITIALIZE LIST FOR FINAL RESULT
if (TotalLine <= 0) // CHECK IF FILE HAS NO DATA
{
Console.WriteLine("No Data found !");
return;
}
double CurrentNumber = double.Parse(data[0]), NextNumber = 0, diff = 0; // INITIALIZE OF LOCAL VARIABLES, CURRENT NUMBER = FIRST NO FROM FILE
for (int cntr = 1; cntr < TotalLine; cntr++) // FOR LOOP FOR EACH LINE
{
NextNumber = double.Parse(data[cntr]); //PARSING NEXT NO
diff = CurrentNumber - NextNumber; // GETTING DIFFERENCE
if (diff <= 100 && diff >= -100) // MATCH THE DIFFERENCE
{
continue; // SKIP THE LOGIC IF DIFF IS LESS THEN 100
}
else
{
FinalList.Add(CurrentNumber.ToString()); // ADDING THE NO TO LIST
CurrentNumber = NextNumber; // POINTING TO NEXT NO
}
}
FinalList.Add(CurrentNumber.ToString()); // ADDING LAST NO.
foreach (string d in FinalList) // FOR EACH LOOP TO PRINT THE FINAL LIST
Console.WriteLine(d);
File.WriteAllLines(outputFile, FinalList); // SAVING TO THE FILE
}
The above program will generate the output is :
200
310
500
Hi there I'm trying to write a method that reads every number in a list and detects where it spikes and drops. This is what I have so far:
I basically figure if I loop through the list, loop through it again to get the next number in the list, then detecting if it's more or less. If it's more it'll save to one list, vice versa.
What I want this method to do is determine where there's a spike of 100 or more, save the point that it does this (which is 'counter') and also save the points where the numbers drop.
This so far notices only a drop and it will save every number in the list until it spikes again and once it has spiked it shows no numbers, until it drops again and so on.
I've put 'check' and 'check2' to try and counteract it saving every number after it notices a drop and only save it once but no luck.
Any ideas?
public void intervalDetection()
{
//Counter is the point in the list
int counter = 0;
int spike = 0;
int drop = 0;
//Loop through power list
for (int i = 0; i < powerList.Count(); i++)
{
counter++;
int firstNumber = powerList[i];
//Loop again to get the number after??
for (int j = 1; j < 2; j++)
{
//Detect Spike
spike = firstNumber + 100;
drop = firstNumber - 100;
if (powerList[j] > spike)
{
if (check2 == false)
{
intervalStartList.Add(counter);
check2 = true;
check = false;
}
}
//Detect Drop
else if (powerList[j] < drop)
{
if (check == false)
{
intervalEndList.Add(counter);
check = true;
check2 = false;
}
}
}
Create integer "average"
Loop through List/Array and add each value to average
Divide average by the count of the List/Array
Loop through List/Array and check deviation to the average integer
derp
Code example:
public class DSDetector {
public static List<int>[] getDropsnSpikes(List<int> values, int deviation) {
List<int> drops = new List<int>();
List<int> spikes = new List<int>();
int average = 0;
foreach (int val in values) {
average += val;
}
average = average/values.Count;
foreach (int val in values) {
if (val < average - deviation) {
drops.add(val);
}
if (val > average + deviation) {
spikes.add(val);
}
}
//derp.
return new List<int>{drops, spikes};
}
}
not tested but I think it works. Just try it.
What exactly do you mean saying "peaks" and "drops"?
Let's say you have following list of integers
112, 111, 113, 250, 112, 111, 1, 113
In this case value 250 is peak and 1 drop relative to average value and you can get it using Kai_Jan_57 answer.
But also 250 is peak to previous value 113 and 112 is drop for 250.
If you want to find local peaks and drops you can check each value relative to previous and next: find average as avg=(val[i-1]+val[i+1])/2 and check if val[i]>avg + 100 (peak) or val[i]
I have a MsSql database which calculates the timespan between two dates in seconds. That works fine. I use this column afterwards in C# and write them in an array.
This array is the input for a chart later on.
So far this works well, but I cannot find a way to display the seconds in a format like hhh:mm:ss as the timespan can be greater than 24h.
I tried ChartArea.AxisY.LabelStyle.Format = "hhmmss"; but it does not work at all.
Does anybody has an idea how I could do that?
EDIT:
I add the data this way:
chart2.Series.Clear();
chart2.ChartAreas.Clear();
Series BoxPlotSeries = new Series();
ChartArea ChartArea2 = new ChartArea();
ChartArea ChartArea3 = new ChartArea();
chart2.ChartAreas.Add(ChartArea2);
chart2.ChartAreas.Add(ChartArea3);
ChartArea2.Name = "Data Chart Area";
ChartArea3.Name = "BoxPlotArea";
BoxPlotSeries.Name = "BoxPlotSeries";
BoxPlotSeries.ChartType = SeriesChartType.BoxPlot;
BoxPlotSeries.ChartArea = "BoxPlotArea";
chart2.Series.Add(BoxPlotSeries);
Series Input1 = new Series();
Input1.Name = "Input1";
Input1.ChartType = SeriesChartType.Point;
Input1.ChartArea = "Data Chart Area";
chart2.Series.Add(Input1);
chart2.Series["Input1"].Points.DataBindY(InputArray);
chart2.ChartAreas["BoxPlotArea"].AxisX.CustomLabels.Add(2, 0.0, "BoxPlot1");
chart2.Series["BoxPlotSeries"]["BoxPlotSeries"] = "Input1";
chart2.Series["BoxPlotSeries"]["BoxPlotShowMedian"] = "true";
chart2.Series["BoxPlotSeries"]["BoxPlotShowUnusualValues"] = "false";
chart2.Series["BoxPlotSeries"]["PointWidth"] = "0.5";
chart2.Series["BoxPlotSeries"].IsValueShownAsLabel = false;
ChartArea2.Visible = false;
ChartArea3.BackColor = Color.FromArgb(224,224,224);
//I tried to format it this way but it didn't work
//ChartArea3.AxisY.LabelStyle.Format = "{0:HHHmmss}";
chart2.ChartAreas["BoxPlotArea"].AxisX.LabelStyle.Angle = -90;
EDIT2:
And here's how I populate the input array
int[] InputArray = new int[1000000];
int c = 0;
con.Open();
dr = cmd.ExecuteReader();
if (dr.HasRows)
{
while (dr.Read())
{
int n;
if (int.TryParse(dr[0].ToString(),out n) == true)
{
InputArray[c] = Convert.ToInt32(dr[0].ToString());
c++;
}
}
}
if (c == 0) { c = 1; }
Array.Resize(ref InputArray, c - 1);
EDIT 3:
The Boxplot should look like this in the end:
In Excel the format to display hours greater than 24 is called "[h]:mm:ss;#"
EDIT4:
Thanks to #TAW I nearly managed to solve my problem. I made some adjustments to his solution and came up with this:
In the chart code block:
The Value "max" is set before.
ChartArea3.AxisY.MajorTickMark.Interval = addCustomLabels(ChartArea3, BoxPlotSeries, 60 * 60, max);
int addCustomLabels(ChartArea ca, Series series, int interval, int max)
{
int tickNo = 0;
ca.AxisY.CustomLabels.Clear();
if(max / interval > 10)
{
interval = (max / 10) - (max / 10) % (60*30);
tickNo = (max / 10) - (max / 10) % (60*30);
}
if (max / interval <= 2 )
{
interval = (max / 4) - (max / 4) % (60 * 15);
tickNo = (max / 4) - (max / 4) % (60 * 15);
}
for (int i = 0; i < max; i += interval)
{
CustomLabel cl = new CustomLabel();
cl.FromPosition = i - interval / 2;
cl.ToPosition = i + interval / 2;
cl.Text = hhh_mm_ss(i);
ca.AxisY.CustomLabels.Add(cl);
}
return tickNo;
}
My problem is now, that sometimes no axis lable (apart from 0:00) is shown even when the code runs through it without any problems.
Has anybody and idea what could be wrong?
Your task involves two parts:
displaying seconds in the hhh:mm:ss format
putting them as labels on the y-axis
There is no suitable date-time formatting string for this in c#, so we can't make use of the built-in automatic labels and their formatting.
There also no way to use expressions that call a function on the automatic labels, unfortunately.
So we can't use those.
Instead we will have to add CustomLabels. This is not very hard but does take a few steps..
But let's start with a function that converts an int to the hhh:mm:ss string we want; this should do the job:
string hhh_mm_ss(int seconds)
{
int sec = seconds % 60;
int min = ((seconds - sec)/60) % 60;
int hhh = (seconds - sec - 60 * min) / 3600;
return hhh > 0 ? string.Format("{2}:{1:00}:{0:00}", sec, min, hhh)
: min + ":" + sec.ToString("00");
}
Maybe it can be optimized, but for our purpose it'll do.
Next we need to create the CustomLabels. They will replace the normal axis labels and we need to add them in a separate loop over the data after each binding.
One special thing about them is their positioning. Which is smack between two values we need to give them: the FromPosition and ToPosition, both in the unit of the axis-values.
Another difference to normal, automatic Labels is that it is up to us to create as many or few of them as we need..
This function tries to create a number that will go up to the maximum y-value and space the CustomLabels at a given interval:
void addCustomLabels(ChartArea ca, Series series, int interval)
{
// we get the maximum form the 1st y-value
int max = (int)series.Points.Select(x => x.YValues[0]).Max();
// we delete any CLs we have
ca.AxisY.CustomLabels.Clear();
// now we add new custom labels
for (int i = 0; i < max; i += interval)
{
CustomLabel cl = new CustomLabel();
cl.FromPosition = i - interval / 2;
cl.ToPosition = i + interval / 2;
cl.Text = hhh_mm_ss(i);
ca.AxisY.CustomLabels.Add(cl);
}
}
The first parameters to call this are obvious; the last one however is tricky:
You need to decide to interval you want your labels to have. It will depend on various details of your chart:
the range of values
the size of the chart area
the size of the font of the axis
I didn't set any special Font in the function; CustomLabels use the same Font as normal axis labels, i.e. AxisY.LabelStyle.Font.
For my screenshot I prepared the chart like this:
ca.AxisX.Minimum = 0;
ca.AxisY.MajorTickMark.Interval = 60 * 60; // one tick per hour
addCustomLabels(ca, s, 60 * 30); // one label every 30 minutes
I have also added DataPoint Labels for testing to show the values..:
series.Points[p].Label = hhh_mm_ss((int)y) + "\n" + y;
Here is the result:
UPDATE: This answer may be quite useful for other readers, but it pretty much misses the OP's issues. I'll leave it as it stands, but it will not help in creating specially formatted y-axis labels..
Most Chart problems stem from invalid or useless x-values. The following discussion tries to help avoiding or getting around them..
A number is a number and you can't simply display it as a DateTime, or for that matter a TimeSpan.
So you need to add the X-Values as either DateTime or as double that contain values that can be converted to DateTime. The fomer is what I prefer..
So instead of adding the seconds directly add them as offsets from a given DateTime:
Change something like this
series.Points.AddXY(sec, yValues);
To this:
var dt = new DateTime(0).AddSeconds(sec);
series.Points.AddXY(dt, yValues);
Now you can use the date and time formatting strings as needed..:
chartArea.AxisX.LabelStyle.Format = "{mm:ss}";
You could also add them as doubles that actually are calculated from DateTimes via the ToOADate:
series.Points.AddXY(dt.ToOADate(), yValues);
But now you will have to set the ChartValueType.DateTime and probably also AxisX.IntervalType and AxisX.Interval to make sure the chart gets the formatting right..:
s.XValueType = ChartValueType.DateTime;
ca.AxisX.Interval = 5;
ca.AxisX.IntervalType = DateTimeIntervalType.Seconds;
ca.AxisX.LabelStyle.Format = "{mm:ss}";
Pick values that suit your data!
Note that the problem with your original code is that the X-Values internally always are doubles, but the seconds are not integer values in them but fractional parts; so you need some kind of calculation. That's what ToOADate does. Here is a short test that shows what one second actually does amount to as a OADate double :
Best add the X-Values as DateTimes so all further processing can rely on the type..
Update I just saw that you have finally added the real code to your question and that is uses Points.DataBindY. This will not create meaningful X-Values, I'm afraid. Try to switch to Points.DataBindXY! Of course the X-Values you bind to also need to follow the rules I have explained above..!
You can do a loop over your array and convert the numbers like I shown above; here is a simple example:
int[] seconds = new int[5] { 1, 3, 88, 123, 3333 };
double[] oaSeconds = seconds.Select(x => new DateTime(0).AddSeconds(x).ToOADate())
.ToArray();
If you are trying to show more than 2 digits of hour I think this should work for you
//yourTimeSpan is the TimeSpan that you already have
var hoursDouble = Math.Floor(yourTimeSpan.TotalHours);
string hours;
string minutes;
string seconds;
//check hours
if(hoursDouble < 10)
{
hours = string.Format("0{0}", hoursDouble);
}
else
{
hours = hoursDouble.ToString();
}
//check minutes
if (yourTimeSpan.Minutes < 10)
{
minutes = string.Format("0{0}", yourTimeSpan.Minutes);
}
else
{
minutes = yourTimeSpan.Minutes.ToString();
}
//check seconds
if (yourTimeSpan.Seconds < 10)
{
seconds = string.Format("0{0}", yourTimeSpan.Seconds);
}
else
{
seconds = yourTimeSpan.Seconds.ToString();
}
string formattedSpan = String.Format("{0}:{1}:{2}", hours, minutes, seconds);
Update: I think this should solve the problem you were seeing with single digit numbers
Let's say we're tracking the times when a user is performing a certain action, and we want to know the average time between said actions.
For example, if the user performed this action at these times:
today, 1 PM
today, 3 PM
today, 6 PM
The result would be 2.5 hours.
I actually have solved this already, but I felt my solution was more complicated than necessary. I'll post it as an answer.
It seems that you are basically looking for Max - Min divided by Count.
public TimeSpan? Average
{
get
{
var diff = _dateTimes.Max().Subtract(_dateTimes.Min());
var avgTs = TimeSpan.FromMilliseconds(diff.TotalMilliseconds / (_dateTimes.Count() - 1));
return avgTs;
}
}
Make sure you check that there is more than one DateTime.
Update: Even more accurate if you use Ticks.
TimeSpan.FromTicks(diff.Ticks / (_dateTimes.Count() - 1));
I recently had a similar task in where I had a long running operation iterating over thousands of rows with 20-30 iterations within each.
void LongRunningOperation()
{
int r = 5000;
int sR = 20;
List<TimeSpan> timeSpanList = new List<TimeSpan>();
for (int i = 0; i < r; i++)
{
DateTime n = DateTime.Now; // Gets start time of this iteration.
for (int x = 0; x < sR; x++)
{
// DOING WORK HERE
}
timeSpanList.Add(DateTime.Now - n); // Gets the length of time of iteration and adds it to list.
double avg = timeSpanList.Select(x => x.TotalSeconds).Average(); // Use LINQ to get an average of the TimeSpan durations.
TimeSpan timeRemaining = DateTime.Now.AddSeconds((r - i) * avg) - DateTime.Now;
// Calculate time remaining by taking the total number of rows minus the number of rows done multiplied by the average duration.
UpdateStatusLabel(timeRemaining);
}
}
This is how I solved it, but I don't like it much:
public class HistoryItem
{
private IEnumerable<DateTime> _dateTimes;
public TimeSpan? Average
{
get {
TimeSpan total = default(TimeSpan);
DateTime? previous = null;
int quotient = 0;
var sortedDates = _dateTimes.OrderBy(x => x);
foreach (var dateTime in sortedDates)
{
if (previous != null)
{
total += dateTime - previous.Value;
}
++quotient;
previous = dateTime;
}
return quotient > 0 ? (TimeSpan.FromMilliseconds(total.TotalMilliseconds/quotient)) as TimeSpan? : null;
}
}
}