Fez Cobra III Analog Input read rates

In other blog posts I have measured the AnalogInput read rate of my Netduino, FEZ Lemur and FEZ Panda III devices and was surprised by some of the numbers. Now, I have another project which uses a GHI Electronics FEZ Covbra III so have done another quick test.

This is just a simple test, not terribly representative of real world I just wanted to get comparable numbers.

public static void Main()
{
   int value;
   AnalogInput x1 = new AnalogInput(FEZLemur.AnalogInput.D19);
   Stopwatch stopwatch = Stopwatch.StartNew();

   Debug.Print("Starting");

   stopwatch.Start();
   for (int i = 0; i <; SampleCount; i++)
   {
      value = x1.ReadRaw();
   }
   stopwatch.Stop();

   Debug.Print("Duration = " + stopwatch.ElapsedMilliseconds.ToString() + " mSec " + (SampleCount * 1000 / stopwatch.ElapsedMilliseconds).ToString() + "/sec";);
}

Fez CobraIII 120 MHz CPU
Duration = 9297 mSec 10756/sec
Duration = 9297 mSec 10756/sec
Duration = 9298 mSec 10755/sec
Duration = 9296 mSec 10757/sec
Duration = 9298 mSec 10755/sec

Something is not quite right here need to look at my code and the numbers some more.

Netduino 3 AnalogInput read rates

At CodeClub some of the students build a power consumption meter and as part of that project we measure the AnalogInput sample rates to check they are sufficient for our application.

Earlier this term when we measured the sampling rates in a CodeClub session we had a mix of Netduino 2 and Netduino 3 devices and some of the results differed from my previous observations. I used the same code on all the devices

int value;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);
stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.ReadRaw();
}
stopwatch.Stop();

Netduino Plus 2

Duration = 2081 mSec 48053/sec
Duration = 2082 mSec 48030/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2082 mSec 48030/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec

Netduino 3

Duration = 2071 mSec 48285/sec
Duration = 2069 mSec 48332/sec
Duration = 2070 mSec 48309/sec
Duration = 2071 mSec 48285/sec
Duration = 2071 mSec 48285/sec
Duration = 2070 mSec 48309/sec
Duration = 2070 mSec 48309/sec
Duration = 2071 mSec 48285/sec
Duration = 2071 mSec 48285/sec
Duration = 2071 mSec 48285/sec

Netduino 3 Ethernet
Duration = 2136 mSec 46816/sec
Duration = 2137 mSec 46794/sec
Duration = 2136 mSec 46816/sec
Duration = 2135 mSec 46838/sec
Duration = 2135 mSec 46838/sec
Duration = 2137 mSec 46794/sec
Duration = 2137 mSec 46794/sec
Duration = 2135 mSec 46838/sec
Duration = 2136 mSec 46816/sec
Duration = 2135 mSec 46838/sec

Netduino 3 Wifii
Duration = 3902 mSec 25627/sec
Duration = 3901 mSec 25634/sec
Duration = 3902 mSec 25627/sec
Duration = 3902 mSec 25627/sec
Duration = 3901 mSec 25634/sec
Duration = 3903 mSec 25621/sec
Duration = 3903 mSec 25621/sec
Duration = 3902 mSec 25627/sec
Duration = 3902 mSec 25627/sec
Duration = 3903 mSec 25621/sec

The results for the Netduino 3 & Netduino 3 Ethernet were comparable with the Netduino Plus 2 in my earlier post. The reduction in the sampling rate of the Netduino 3 Wifi warrants some further investigation.

Energy Monitor Shield RMS Calculation

The voltage output by the current sensor and measured by the Netduino needs to be corrected using the offset value then the RMS value calculated. This RMS value then needs to be adjusted taking into account the voltage range of the Netduino analog input (0V-3V3), the resolution of the analog input (12 bits) and the voltage output by the non-invasive current sensor (0~1V for 0~30A).

My approach appears to produce reasonable values but I will need to compare them with a calibrated reference device to check its accuracy. The 18W measurement with no current flowing is due to the noise on the analog input discussed in an earlier post.

The first version of the software used the initial offset value, the second version updates the offset value at the end of each set of samples.

int valueSum = 0;
int valueSumSqr = 0;
int offset;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

// Calculate the sum for initial offset
for (int i = 0; i < SampleCount; i++)
{
valueSum += x1.ReadRaw();
}
offset = valueSum / SampleCount;

Stopwatch stopwatch = Stopwatch.StartNew();
stopwatch.Start();

for (int i = 0; i < SampleCount; i++)
{
int value = x1.ReadRaw();

value -= offset;

valueSumSqr += (value * value);
}
stopwatch.Stop();

double rms = System.Math.Sqrt((double)(valueSumSqr / SampleCount));
double rmsCurrent = rms * (3.3 / 4096.0) * 3.3 * 30.0;
double rmsWatts = rmsCurrent * 230;

Duration = 2587 mSec 38654/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2587 mSec 38654/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2587 mSec 38654/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2587 mSec 38654/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2587 mSec 38654/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2588 mSec 38639/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2587 mSec 38654/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2587 mSec 38654/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2587 mSec 38654/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2587 mSec 38654/sec RMS 1.0 RMS Current 0.1A RMS Power 18W

int valueSum = 0;
int valueSumSqr = 0;
int offset;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);
// Calculate the sum for initial offset
for (int i = 0; i < SampleCount; i++)
{
valueSum += x1.ReadRaw();
}
offset = valueSum / SampleCount;

Stopwatch stopwatch = Stopwatch.StartNew();
stopwatch.Start();

for (int i = 0; i < SampleCount; i++)
{
int value = x1.ReadRaw();

valueSum += value;

value -= offset;

valueSumSqr += (value * value);
}
stopwatch.Stop();

offset = valueSum / SampleCount;

double rms = System.Math.Sqrt((double)(valueSumSqr / SampleCount));
double rmsCurrent = rms * (3.3 / 4096.0) * 3.3 * 30.0 ;
double rmsWatts = rmsCurrent * 230;

Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W
Duration = 2816 mSec 35511/sec RMS 1.0 RMS Current 0.1A RMS Power 18W

Both versions appear to sample the output of the non-invasive current sensor at a more than sufficient rate.

Energy Monitor Shield Analog Input Noise

While writing the calibration code I noticed that the voltage reading was a bit noisy so I modified the code to record the minimum & maximum values then put the current sensor clamp on a wire not carrying any current.

int value;
int valueSum = 0;
int valueMinimum = int.MaxValue;
int valueMaximum = int.MinValue;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.ReadRaw();

if (value < valueMinimum)
{
valueMinimum = value;
}
if (value > valueMaximum)
{
valueMaximum = value;
}
valueSum += value;
}
stopwatch.Stop();

Duration = 3509 mSec 28498/sec Min=2031 Max=2052 Avg=2041
Duration = 3501 mSec 28563/sec Min=2031 Max=2052 Avg=2041
Duration = 3500 mSec 28571/sec Min=2031 Max=2053 Avg=2041
Duration = 3501 mSec 28563/sec Min=2031 Max=2053 Avg=2041
Duration = 3500 mSec 28571/sec Min=2031 Max=2051 Avg=2041
Duration = 3500 mSec 28571/sec Min=2031 Max=2052 Avg=2041
Duration = 3500 mSec 28571/sec Min=2031 Max=2053 Avg=2041
Duration = 3501 mSec 28563/sec Min=2032 Max=2053 Avg=2041
Duration = 3500 mSec 28571/sec Min=2031 Max=2053 Avg=2041
Duration = 3500 mSec 28571/sec Min=2030 Max=2052 Avg=2041

Looks like there is a little bit of noise on the input when there is no current flowing.

I also tried using the baked in Min & Max functions but these were a bit slower which was not what I was expecting

int value;
int valueSum = 0;
int valueMinimum = int.MaxValue;
int valueMaximum = int.MinValue;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.ReadRaw();

valueMinimum = System.Math.Min(value, valueMinimum);
valueMaximum = System.Math.Max(value, valueMaximum);

valueSum += value;
}
stopwatch.Stop();

Duration = 4672 mSec 21390/sec Min=2036 Max=2048 Avg=2041
Duration = 4665 mSec 21436/sec Min=2036 Max=2049 Avg=2041
Duration = 4665 mSec 21436/sec Min=2035 Max=2049 Avg=2041
Duration = 4664 mSec 21440/sec Min=2036 Max=2048 Avg=2041
Duration = 4664 mSec 21440/sec Min=2036 Max=2048 Avg=2041
Duration = 4664 mSec 21440/sec Min=2036 Max=2049 Avg=2041
Duration = 4664 mSec 21440/sec Min=2035 Max=2048 Avg=2041
Duration = 4664 mSec 21440/sec Min=2035 Max=2048 Avg=2041
Duration = 4664 mSec 21440/sec Min=2035 Max=2049 Avg=2041
Duration = 4664 mSec 21440/sec Min=2035 Max=2048 Avg=2041

Energy Monitor Shield Sensor Calibration

The Netduino 2 Plus AnalogInput has a range of 0V to 3V3. The Non Invasive Current Sensor we are using returns 0-1V AC for 0-30 Amps AC. To measure the sensor’s output waveform the Energy Monitor Shield uses a voltage divider to offset the reference voltage to 3v3/2. To calibrate the sensor we averaged the input voltage and over 100,000 readings.

The initial code looked like this

int value;
int valueSum = 0;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.ReadRaw();

valueSum = valueSum + value;
}
stopwatch.Stop();

Duration = 2272 mSec 44014/sec
Duration = 2273 mSec 43994/sec
Duration = 2272 mSec 44014/sec
Duration = 2272 mSec 44014/sec
Duration = 2272 mSec 44014/sec
Duration = 2272 mSec 44014/sec
Duration = 2272 mSec 44014/sec
Duration = 2272 mSec 44014/sec
Duration = 2272 mSec 44014/sec
Duration = 2272 mSec 44014/sec

I then modified the code to not use a temporary variable

int valueSum = 0;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
valueSum = valueSum + x1.ReadRaw();
}
stopwatch.Stop();

Duration = 2181 mSec 45829/sec
Duration = 2182 mSec 45829/sec
Duration = 2181 mSec 45829/sec
Duration = 2181 mSec 45829/sec
Duration = 2181 mSec 45829/sec
Duration = 2181 mSec 45829/sec
Duration = 2181 mSec 45829/sec
Duration = 2181 mSec 45829/sec
Duration = 2181 mSec 45829/sec
Duration = 2181 mSec 45829/sec

The code without the temporary variable was slightly faster.

 

Netduino AnalogInput read rates

At CodeClub some of the students are building a netduino based power consumption monitor using an Energy Monitor Shield (with my modifications). The approach for the software was “inspired” by the Arduino code developed by the Open Energy Monitor project. First step was to confirm that the Netduino 2 Plus we were using could sample the Alternating Current(AC) waveform often enough.

The first version of the code called the Microsoft.Spot.Hardware.AnalogInput.Read method (which returns a floating point number) 100,000 times. The duration was measured using a Stopwatch class written by ChrisW of Secretlabs.

double value;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);

Stopwatch stopwatch = Stopwatch.StartNew();
stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.Read();
}
stopwatch.Stop();

Duration = 5496 mSec 18195/sec
Duration = 5497 mSec 18191/sec
Duration = 5496 mSec 18195/sec
Duration = 5496 mSec 18195/sec
Duration = 5497 mSec 18191/sec
Duration = 5496 mSec 18195/sec
Duration = 5496 mSec 18195/sec
Duration = 5496 mSec 18195/sec
Duration = 5496 mSec 18195/sec
Duration = 5497 mSec 18191/sec

The AnalogPort also has a overloaded Read method which returns an integer

int value;
AnalogInput x1 = new AnalogInput(Cpu.AnalogChannel.ANALOG_0);
stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.ReadRaw();
}
stopwatch.Stop();

Duration = 2081 mSec 48053/sec
Duration = 2082 mSec 48030/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2082 mSec 48030/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec
Duration = 2081 mSec 48053/sec

There is also a Secret labs AnalogInput which has a Read method which returns an integer

int value;
SecretLabs.NETMF.Hardware.AnalogInput x1 = new SecretLabs.NETMF.Hardware.AnalogInput(Pins.GPIO_PIN_A0);
stopwatch.Start();
for (int i = 0; i < SampleCount; i++)
{
value = x1.Read();
}
stopwatch.Stop();

Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8564 mSec 11676/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec
Duration = 8563 mSec 11678/sec

The int Microsoft.Spot.Hardware.AnalogInput.ReadRaw() appears to be  quite a bit faster than the other two approaches.

SQL Azure Database performance oddness

For a few years I have worked on a midsize Azure application for managing promotional vouchers which are delivered via SMS or email and redeemed via software integrated into the point of sale terminal software of several local vendors.

A promotion has a batch of vouchers which are available for allocation to consumers and I had noticed from the logs and performance counters that the process of getting the next voucher slowed down significantly as the number of available vouchers decreased.

Voucher batches range in size from 100’s to 100,000’s of vouchers and the performance of a small promotion for a local wine shop was where I initially noticed how much the duration increased. The promotion had roughly 850 vouchers the allocation of the first vouchers took 10’s of mSec each but the last 10 vouchers sometimes took more than 1000mSec each.

I took a copy of the live database and removed the customer data so I could explore the performance of my TSQL in a controlled environment. I initially downloaded a copy of the database to one of my development servers and tried to simulate the problem while monitoring performance using SQL Profiler and other tools but the allocation time was fast and consistent.

The database performance appeared to be a SQL Azure specific issue so I built a cut back web role test harness which called the underlying stored procedure so I could closely monitor the performance. The test harness could make a specified number of calls recording the duration of each call and the overall duration. I then de allocated all the vouchers in the wine shop promotion and allocated them in chunks(all durations are in mSec and are the average time it takes to make a single call)

100 vouchers at a time (0 – 800 of 843 vouchers)

43, 88, 136, 191, 260, 305, 358, 379

10 vouchers at a time (800-840 of 843 vouchers)

431, 440, 404, 412

1 voucher at a time (840-843 of 843 vouchers, last one is failure)

400, 423,404, 390

After some debugging, progressive removal of code and looking at query plans I identified the problematic TSQL statement.

UPDATE TOP(1) Voucher SET
Voucher.AllocatedByActivity = @ActivityUID,
Voucher.AllocatedAtUTC = @RequestDateTimeUTC,
Voucher.AllocationReference = @ClientReference,
@VoucherCode = Voucher.Code
WHERE (( Voucher.VoucherBatchUID = @VoucherBatchUID ) AND ( Voucher.AllocatedByActivity IS NULL ))

This update statement uses a single statement transaction to get the next random un-allocated voucher code in the specified voucher batch.

After some conversations with a SQL Azure support engineer at Microsoft (who was very helpful) he figured out that in SQL Azure the query processor needed a query hint to tell it to use an existing index to make it perform consistently.

UPDATE TOP(1) Voucher SET
Voucher.AllocatedByActivity = @ActivityUID,
Voucher.AllocatedAtUTC = @RequestDateTimeUTC,
Voucher.AllocationReference = @ClientReference,
@VoucherCode = Voucher.Code
FROM Voucher WITH (INDEX(IX_VoucherByVoucherBatchUIDAllocatedByActivity))
WHERE (( Voucher.VoucherBatchUID = @VoucherBatchUID ) AND ( Voucher.AllocatedByActivity IS NULL ))

100 vouchers at a time (0 – 800 of 843 vouchers)

20, 8, 13, 9, 32, 7, 15, 9

10 vouchers at a time (800-840 of 843 vouchers)

17, 9, 14, 13

1 voucher at a time (840-843 of 843 vouchers, last one is failure)

12,11,13, 5

I always have plenty of performance counters and logging (using the enterprise library) on my Azure web and worker roles but I was also lucky that I noticed something odd in the logs while checking on the progress of another promotion. I actively monitor the performance of my Azure applications as over time the performance characteristics of the underlying hardware will change as fixes and enhancements are released.