finished basic oscilloscope functionalities (max sampling rate seems to be 38KHz)
This commit is contained in:
parent
691ce28d95
commit
d0ef4ee9f9
@ -5,12 +5,14 @@
|
|||||||
#define OWPin 2
|
#define OWPin 2
|
||||||
#define InterruptNumber 0 // Must correspond to the OWPin to correctly detect state changes. On Arduino Uno, interrupt 0 is for digital pin 2
|
#define InterruptNumber 0 // Must correspond to the OWPin to correctly detect state changes. On Arduino Uno, interrupt 0 is for digital pin 2
|
||||||
|
|
||||||
const int SkipSamples = 8; // how many samples we want to skip between two samples we keep (can be used to lower the sampling frequency)
|
// how many samples we want to skip between two samples we keep (can be used to lower the sampling frequency)
|
||||||
const int BufferSize = 128;
|
#define SkipSamples 0
|
||||||
|
|
||||||
|
const int BufferSize = 512;
|
||||||
byte buffer1[BufferSize];
|
byte buffer1[BufferSize];
|
||||||
byte buffer2[BufferSize];
|
byte buffer2[BufferSize];
|
||||||
byte* backBuffer = buffer1;
|
byte* backBuffer = buffer1;
|
||||||
volatile byte backBufferPos = 0;
|
volatile short backBufferPos = 0;
|
||||||
byte samplesSkipped = SkipSamples;
|
byte samplesSkipped = SkipSamples;
|
||||||
unsigned long backBufferStartTime = micros();
|
unsigned long backBufferStartTime = micros();
|
||||||
|
|
||||||
@ -24,7 +26,7 @@ void setup()
|
|||||||
|
|
||||||
digitalWrite(LEDPin, LOW);
|
digitalWrite(LEDPin, LOW);
|
||||||
|
|
||||||
attachInterrupt(InterruptNumber,onewireInterrupt,CHANGE);
|
//attachInterrupt(InterruptNumber,onewireInterrupt,CHANGE);
|
||||||
|
|
||||||
cli();//disable interrupts
|
cli();//disable interrupts
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ void setup()
|
|||||||
ADMUX |= (1 << REFS0); //set reference voltage
|
ADMUX |= (1 << REFS0); //set reference voltage
|
||||||
ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
|
ADMUX |= (1 << ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only
|
||||||
|
|
||||||
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); //set ADC clock with 128 prescaler- 16mHz/128=125kHz ; 13 cycles for a conversion which means 9600 samples per second
|
ADCSRA |= (1 << ADPS2) | (0 << ADPS1) | (1 << ADPS0); //set ADC clock with 32 prescaler- 16mHz/32=500KHz ; 13 cycles for a conversion which means 38000 samples per second
|
||||||
ADCSRA |= (1 << ADATE); //enabble auto trigger
|
ADCSRA |= (1 << ADATE); //enabble auto trigger
|
||||||
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
|
ADCSRA |= (1 << ADIE); //enable interrupts when measurement complete
|
||||||
ADCSRA |= (1 << ADEN); //enable ADC
|
ADCSRA |= (1 << ADEN); //enable ADC
|
||||||
@ -44,7 +46,7 @@ void setup()
|
|||||||
|
|
||||||
sei();//enable interrupts
|
sei();//enable interrupts
|
||||||
|
|
||||||
Serial.begin(200000);
|
Serial.begin(400000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
@ -52,27 +54,32 @@ void loop()
|
|||||||
while(backBufferPos < BufferSize / 2) ;
|
while(backBufferPos < BufferSize / 2) ;
|
||||||
cli();//disable interrupts
|
cli();//disable interrupts
|
||||||
byte* currentBuffer = backBuffer;
|
byte* currentBuffer = backBuffer;
|
||||||
unsigned long currentBufferStartTime = backBufferStartTime;
|
short currentBufferSize = backBufferPos;
|
||||||
byte currentBufferSize = backBufferPos;
|
|
||||||
backBuffer = (backBuffer == buffer1 ? buffer2 : buffer1);
|
backBuffer = (backBuffer == buffer1 ? buffer2 : buffer1);
|
||||||
backBufferPos = 0;
|
backBufferPos = 0;
|
||||||
backBufferStartTime = micros();
|
|
||||||
sei();//enable interrupts
|
sei();//enable interrupts
|
||||||
|
unsigned long currentBufferStartTime = backBufferStartTime;
|
||||||
|
backBufferStartTime = micros();
|
||||||
|
digitalWrite(LEDPin, LOW);
|
||||||
|
|
||||||
|
//Serial.write(currentBuffer, currentBufferSize);
|
||||||
oscilloscope.write(currentBuffer, currentBufferSize, currentBufferStartTime);
|
oscilloscope.write(currentBuffer, currentBufferSize, currentBufferStartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
ISR(ADC_vect) {//when new ADC value ready
|
ISR(ADC_vect) {//when new ADC value ready
|
||||||
byte sample = ADCH; //store 8 bit value from analog pin 0
|
byte sample = ADCH; //store 8 bit value from analog pin 0
|
||||||
|
|
||||||
|
#if SkipSamples > 0
|
||||||
if(samplesSkipped++ < SkipSamples)
|
if(samplesSkipped++ < SkipSamples)
|
||||||
return;
|
return;
|
||||||
samplesSkipped = 0;
|
samplesSkipped = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
backBuffer[backBufferPos++] = sample;
|
backBuffer[backBufferPos++] = sample;
|
||||||
if(backBufferPos >= BufferSize)
|
if(backBufferPos >= BufferSize)
|
||||||
{
|
{
|
||||||
// overflow of back buffer, we loose the current sample
|
// overflow of back buffer, we loose the current sample
|
||||||
|
digitalWrite(LEDPin, HIGH);
|
||||||
backBufferPos = BufferSize - 1;
|
backBufferPos = BufferSize - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ namespace SerialMonitor
|
|||||||
|
|
||||||
Serial = new SerialPort();
|
Serial = new SerialPort();
|
||||||
Serial.PortName = "COM4";
|
Serial.PortName = "COM4";
|
||||||
Serial.BaudRate = 200000;
|
Serial.BaudRate = 400000;
|
||||||
|
|
||||||
Serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(OnDataReceived);
|
Serial.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(OnDataReceived);
|
||||||
|
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
<Window x:Class="SerialMonitor.MainWindow"
|
<Window x:Class="SerialMonitor.MainWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
Title="Serial Monitor" Height="396" Width="629">
|
Title="Serial Monitor" Height="490.75" Width="923">
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextBlock TextWrapping="Wrap" VerticalAlignment="Bottom" Height="150" Text="{Binding ConsoleText}" Style="{DynamicResource Console}" Grid.ColumnSpan="2"/>
|
<Grid.RowDefinitions>
|
||||||
<ScrollViewer HorizontalScrollBarVisibility="Visible" Margin="0,0,0,155" Grid.ColumnSpan="2">
|
<RowDefinition Height="290" />
|
||||||
<Canvas Name="Oscilloscope" Style="{DynamicResource Oscilloscope}"/>
|
<RowDefinition Height="5" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Canvas Name="Oscilloscope" Style="{DynamicResource Oscilloscope}" Margin="0,0,0,17" />
|
||||||
|
<ScrollBar Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Stretch" Height="17" VerticalAlignment="Bottom" Minimum="{Binding MinTime}" Maximum="{Binding MaxTime}" ViewportSize="{Binding ViewportTimeWidth}" Value="{Binding ScrollValue}"/>
|
||||||
|
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch" />
|
||||||
|
<ScrollViewer Grid.Row="2">
|
||||||
|
<TextBlock TextWrapping="Wrap" Style="{DynamicResource Console}" Text="{Binding ConsoleText}"/>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
@ -26,6 +26,37 @@ namespace SerialMonitor
|
|||||||
var context = new MainWindowContext(this);
|
var context = new MainWindowContext(this);
|
||||||
context.WriteLine("Connecting...");
|
context.WriteLine("Connecting...");
|
||||||
this.DataContext = context;
|
this.DataContext = context;
|
||||||
|
|
||||||
|
this.MouseWheel += OnMouseWheel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMouseWheel(object sender, MouseWheelEventArgs e)
|
||||||
|
{
|
||||||
|
MainWindowContext context = (MainWindowContext)this.DataContext;
|
||||||
|
Point cursorPos = e.GetPosition(context.OscilloscopeCanvas);
|
||||||
|
if (cursorPos.X < 0 || cursorPos.X > context.OscilloscopeCanvas.ActualWidth || cursorPos.Y < 0 || cursorPos.Y > context.OscilloscopeCanvas.ActualHeight)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double cursorPosRatio = cursorPos.X / context.OscilloscopeCanvas.ActualWidth;
|
||||||
|
double cursorTime = context.ViewportStartTime + cursorPosRatio * context.ViewportTimeWidth;
|
||||||
|
|
||||||
|
double newTimeWidth = context.ViewportTimeWidth;
|
||||||
|
if (e.Delta > 0)
|
||||||
|
newTimeWidth /= e.Delta * 0.01;
|
||||||
|
else if (e.Delta < 0)
|
||||||
|
newTimeWidth *= -e.Delta * 0.01;
|
||||||
|
|
||||||
|
double totalTimeWidth = Math.Max(0.1, context.MaxTime - context.MinTime);
|
||||||
|
if (newTimeWidth > totalTimeWidth)
|
||||||
|
newTimeWidth = totalTimeWidth;
|
||||||
|
|
||||||
|
double newStartTime = cursorTime - cursorPosRatio * newTimeWidth;
|
||||||
|
if (newStartTime < context.MinTime)
|
||||||
|
newStartTime = context.MinTime;
|
||||||
|
if (newStartTime + newTimeWidth > context.MaxTime)
|
||||||
|
newStartTime = context.MaxTime - newTimeWidth;
|
||||||
|
|
||||||
|
context.SetViewport(newStartTime, newTimeWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
namespace SerialMonitor
|
namespace SerialMonitor
|
||||||
@ -28,11 +30,14 @@ namespace SerialMonitor
|
|||||||
public void WriteLine(string line)
|
public void WriteLine(string line)
|
||||||
{
|
{
|
||||||
Lines.Add(line);
|
Lines.Add(line);
|
||||||
if (Lines.Count > 9)
|
if (Lines.Count > 100)
|
||||||
Lines.RemoveAt(0);
|
Lines.RemoveAt(0);
|
||||||
OnPropertyChanged("ConsoleText");
|
OnPropertyChanged("ConsoleText");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double SampleDelay { get { return 0.000026; } }
|
||||||
|
public int MaxSequenceSize { get { return 2048; } }
|
||||||
|
|
||||||
private class Sequence
|
private class Sequence
|
||||||
{
|
{
|
||||||
public ulong StartTime { get; set; }
|
public ulong StartTime { get; set; }
|
||||||
@ -46,7 +51,11 @@ namespace SerialMonitor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
double ValueToHeight(byte value) { return (double)value + 10; }
|
double ValueToHeight(double time, byte value)
|
||||||
|
{
|
||||||
|
//value = (byte)(Math.Sin(time * 100.0) * 127.0 + 128.0);
|
||||||
|
return 256.0 - (double)value + 10;
|
||||||
|
}
|
||||||
private SortedSet<Sequence> Sequences = new SortedSet<Sequence>(new Sequence.Comparer());
|
private SortedSet<Sequence> Sequences = new SortedSet<Sequence>(new Sequence.Comparer());
|
||||||
public IEnumerable<Line> Oscilloscope
|
public IEnumerable<Line> Oscilloscope
|
||||||
{
|
{
|
||||||
@ -55,63 +64,146 @@ namespace SerialMonitor
|
|||||||
if(!Sequences.Any())
|
if(!Sequences.Any())
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
ulong startTime = Sequences.ElementAt(0).StartTime;
|
|
||||||
|
|
||||||
foreach (var sequence in Sequences)
|
foreach (var sequence in Sequences)
|
||||||
{
|
{
|
||||||
foreach (var line in ConvertToLines(sequence, startTime))
|
double seqStartTime = (double)sequence.StartTime/1000000.0;
|
||||||
|
if (seqStartTime + (double)sequence.Data.Length * SampleDelay > ViewportStartTime && seqStartTime < ViewportStartTime + ViewportTimeWidth)
|
||||||
|
{
|
||||||
|
foreach (var line in ConvertToLines(sequence))
|
||||||
yield return line;
|
yield return line;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IEnumerable<Line> ConvertToLines(Sequence sequence, ulong displayStartTime)
|
private IEnumerable<Line> ConvertToLines(Sequence sequence)
|
||||||
{
|
{
|
||||||
double sampleDelay = 0.000936; // in seconds
|
double viewportWidth = OscilloscopeCanvas.ActualWidth;
|
||||||
double scale = 10; // in pixels per second
|
double scale = viewportWidth / ViewportTimeWidth; // in pixels per second
|
||||||
|
|
||||||
double pos = (sequence.StartTime - displayStartTime) / 1000000.0 * scale;
|
ulong displayStartTime = (ulong)(viewportStartTime_ * 1000000.0);
|
||||||
|
|
||||||
|
double pos = ((double)sequence.StartTime - (double)displayStartTime) / 1000000.0 * scale;
|
||||||
if (pos > 1000)
|
if (pos > 1000)
|
||||||
yield break;
|
yield break;
|
||||||
double prevHeight = ValueToHeight(sequence.Data[0]);
|
double prevPos = pos;
|
||||||
|
byte minValue = sequence.Data[0];
|
||||||
|
byte maxValue = minValue;
|
||||||
|
int prevIdx = 0;
|
||||||
|
double prevHeight = ValueToHeight(sequence.StartTime / 1000000.0, minValue);
|
||||||
for (int idx = 1; idx < sequence.Data.Length; ++idx)
|
for (int idx = 1; idx < sequence.Data.Length; ++idx)
|
||||||
{
|
{
|
||||||
byte value = sequence.Data[idx];
|
byte value = sequence.Data[idx];
|
||||||
|
pos += SampleDelay * scale;
|
||||||
|
if (value > maxValue) maxValue = value;
|
||||||
|
if (value < minValue) minValue = value;
|
||||||
|
|
||||||
|
if (pos > 0 && pos < viewportWidth && pos - prevPos >= 5 || idx == sequence.Data.Length - 1)
|
||||||
|
{
|
||||||
var line = new Line();
|
var line = new Line();
|
||||||
line.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
|
line.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
|
||||||
line.X1 = pos;
|
|
||||||
pos += sampleDelay * scale;
|
|
||||||
line.X2 = pos;
|
|
||||||
line.Y1 = prevHeight;
|
|
||||||
prevHeight = ValueToHeight(value);
|
|
||||||
line.Y2 = prevHeight;
|
|
||||||
line.HorizontalAlignment = HorizontalAlignment.Left;
|
line.HorizontalAlignment = HorizontalAlignment.Left;
|
||||||
line.VerticalAlignment = VerticalAlignment.Center;
|
line.VerticalAlignment = VerticalAlignment.Center;
|
||||||
line.StrokeThickness = 1;
|
line.StrokeThickness = 1;
|
||||||
|
|
||||||
|
line.X1 = prevPos;
|
||||||
|
prevPos = pos;
|
||||||
|
line.X2 = pos;
|
||||||
|
|
||||||
|
double time = (double)sequence.StartTime / 1000000.0 + (double)idx * SampleDelay;
|
||||||
|
double lastHeight = ValueToHeight(time, value);
|
||||||
|
|
||||||
|
if (idx == prevIdx + 1)
|
||||||
|
{
|
||||||
|
line.Y1 = prevHeight;
|
||||||
|
line.Y2 = lastHeight;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (value - minValue > maxValue - value)
|
||||||
|
{
|
||||||
|
line.Y1 = ValueToHeight(time, minValue);
|
||||||
|
line.Y2 = ValueToHeight(time, maxValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line.Y1 = ValueToHeight(time, maxValue);
|
||||||
|
line.Y2 = ValueToHeight(time, minValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prevHeight = lastHeight;
|
||||||
|
minValue = value;
|
||||||
|
maxValue = value;
|
||||||
|
prevIdx = idx;
|
||||||
|
|
||||||
yield return line;
|
yield return line;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void AddSequence(ulong startTime, byte[] data)
|
public void AddSequence(ulong startTime, byte[] data)
|
||||||
{
|
{
|
||||||
|
// TODO: merge sequences if total size is lower than MaxSequenceSize
|
||||||
var sequence = new Sequence { StartTime = startTime, Data = data };
|
var sequence = new Sequence { StartTime = startTime, Data = data };
|
||||||
Sequences.Add(sequence);
|
Sequences.Add(sequence);
|
||||||
OnPropertyChanged("Oscilloscope");
|
OnPropertyChanged("Oscilloscope");
|
||||||
var canvas = (Canvas)Window.FindName("Oscilloscope");
|
OnPropertyChanged("MinTime");
|
||||||
/*canvas.Children.Clear();
|
OnPropertyChanged("MaxTime");
|
||||||
foreach (var line in Oscilloscope)
|
if (Sequences.Count == 1)
|
||||||
canvas.Children.Add(line);*/
|
{
|
||||||
foreach (var line in ConvertToLines(sequence, Sequences.ElementAt(0).StartTime))
|
ViewportStartTime = MinTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
var canvas = OscilloscopeCanvas;
|
||||||
|
foreach (var line in ConvertToLines(sequence))
|
||||||
canvas.Children.Add(line);
|
canvas.Children.Add(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RefreshOscilloscope()
|
||||||
|
{
|
||||||
|
var canvas = OscilloscopeCanvas;
|
||||||
|
canvas.Children.Clear();
|
||||||
|
foreach (var line in Oscilloscope)
|
||||||
|
canvas.Children.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Canvas oscilloscopeCanvas_;
|
||||||
|
public Canvas OscilloscopeCanvas { get { if (oscilloscopeCanvas_ == null) oscilloscopeCanvas_ = (Canvas)Window.FindName("Oscilloscope"); return oscilloscopeCanvas_; } }
|
||||||
|
|
||||||
|
public double MinTime { get { return Sequences.Any() ? (double)Sequences.First().StartTime / 1000000.0 : 0.0; } }
|
||||||
|
public double MaxTime { get { return Sequences.Any() ? Math.Max(MinTime + 0.1, (double)Sequences.Last().StartTime / 1000000.0) : 0.1; } }
|
||||||
|
private double viewportTimeWidth_ = 0.1;
|
||||||
|
public double ViewportTimeWidth
|
||||||
|
{
|
||||||
|
get { return viewportTimeWidth_; }
|
||||||
|
set { viewportTimeWidth_ = value; OnPropertyChanged("ViewportTimeWidth"); RefreshOscilloscope(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private double viewportStartTime_ = 0;
|
||||||
|
public double ViewportStartTime
|
||||||
|
{
|
||||||
|
get { return viewportStartTime_; }
|
||||||
|
set { viewportStartTime_ = value; OnPropertyChanged("ViewportStartTime"); RefreshOscilloscope(); }
|
||||||
|
}
|
||||||
|
public double ScrollValue
|
||||||
|
{
|
||||||
|
get { return MathEx.Unproject(MathEx.Project(ViewportStartTime, MinTime, MaxTime - ViewportTimeWidth), MinTime, MaxTime); }
|
||||||
|
set { ViewportStartTime = MathEx.Unproject(MathEx.Project(value, MinTime, MaxTime), MinTime, MaxTime - ViewportTimeWidth); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetViewport(double startTime, double timeWidth)
|
||||||
|
{
|
||||||
|
viewportStartTime_ = startTime;
|
||||||
|
ViewportTimeWidth = timeWidth;
|
||||||
|
}
|
||||||
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
protected void OnPropertyChanged(string name)
|
protected void OnPropertyChanged(string name)
|
||||||
{
|
{
|
||||||
PropertyChangedEventHandler handler = PropertyChanged;
|
if (PropertyChanged != null)
|
||||||
if (handler != null)
|
|
||||||
{
|
{
|
||||||
handler(this, new PropertyChangedEventArgs(name));
|
PropertyChanged(this, new PropertyChangedEventArgs(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
SerialMonitor/SerialMonitor/MathEx.cs
Normal file
21
SerialMonitor/SerialMonitor/MathEx.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SerialMonitor
|
||||||
|
{
|
||||||
|
public static class MathEx
|
||||||
|
{
|
||||||
|
public static double Project(double value, double rangeMin, double rangeMax)
|
||||||
|
{
|
||||||
|
return (value - rangeMin) / (rangeMax - rangeMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Unproject(double ratio, double rangeMin, double rangeMax)
|
||||||
|
{
|
||||||
|
return rangeMin + ratio * (rangeMax - rangeMin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -54,6 +54,7 @@
|
|||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</ApplicationDefinition>
|
</ApplicationDefinition>
|
||||||
<Compile Include="MainWindowContext.cs" />
|
<Compile Include="MainWindowContext.cs" />
|
||||||
|
<Compile Include="MathEx.cs" />
|
||||||
<Compile Include="SerialMessage.cs" />
|
<Compile Include="SerialMessage.cs" />
|
||||||
<Compile Include="SerialPortExtensions.cs" />
|
<Compile Include="SerialPortExtensions.cs" />
|
||||||
<Page Include="MainWindow.xaml">
|
<Page Include="MainWindow.xaml">
|
||||||
|
Loading…
Reference in New Issue
Block a user