News:

Let's find out together what makes a PIC Tick!

Main Menu

Scaled signed words

Started by TimB, Apr 26, 2021, 12:37 PM

Previous topic - Next topic

TimB


Hi all

On a previous project I though I finished I came across a strange anomaly.
What I found was an odd discrepancy between 2 stored values.

First I think I better explain what and how the code is working
1 I need to store 10 mins worth of temperature logs, There are 2 sets of logs, 1 for each channel so were talking 1200 logs. If they were stored as Floats then your looking at 4800 bytes. Well over the ram I have spare. So I stored them as raw ADC values that come in as words that reduces it to a more manageable 2400 bytes. When I need to read them I do the conversion on the fly so I get the accurate float value of the number.

2 I have a 10 second pre ring buffer that I copy into the larger buffer at the start of the "logging" of the temperatures. I mark the start location in the buffer to indicate the Temperature log start.
I also log the true Temperature start value in its current float form.

What I found was a miss match between the reconstructed temperature from the buffer and the recorded current temperature I saved.

I spent a lot of time looking through the code and really have no idea how it did it and cannot confirm my code is right. With all the pointers and copying data around I could have messed up. So I did extensive testing to see if I could replicate it. I cannot.
That does not make me happy. So I'm looking to change the way the code works. I want to work with only the pre converted numbers. Take the float and convert that to a scaled word.

This where I'm stuck. The temperature can be -50.0 to +280.0 as a float

What would be the best way to convert that to a signed word. Or should I not bother with the sign and just work as a scaled number. Not sure on the best method on doing that so am utilising the hive mind of the forum.

BTW at certain times I need to get an average of one of the channels. My current code is not efficient with having to do all the conversions from the raw word to Resistance to Ohms to Temp and then the zero and span so any conversion from a float to s word variable will not be that time consuming in comparison.

Thanks

Tim
Below is why I store all the data, its so I can produce a graph and adjust the start end end Temperature pointers.Screenshot 2021-04-26 13.34.41.png



top204

#1
The fRound function will convert a Floating point to an integer, signed or unsigned.

Try this code to see the results:
    Dim Floatin As Float
    Dim Wordout As SWord
   
    For Floatin = -50.0 To 280.0 Step 0.1
        Wordout = fRound(Floatin)
        HRSOutLn "Float= ", Dec1 Floatin, ": Integer= ", SDec Wordout
    Next

The fRound function will round up and down depending on the value within the floating point variable. For example, a value of 45.54 will round up to 46, but a value of 45.42 will round down to 45. i.e. above or below the half way threshold.

To only round down, use an assignment such as MySWord = MyFloat.

Make sure you place "Declare Float_Display_Type = Fast" in the program to display more accurate floating point values, and display them faster.

To actually scale from one set of values to another, load the "Scale.inc" library into the program and use the fScale procedure to scale floating point values, then make the assignment a signed integer variable.

TimB


Thanks Les!!

I will try various versions of that to see what works best.
I will use the * 10 and then convert. I was worried that as its signed it would cause and issue.

Tim

John Drew

Tim, I've had to do this kind of thing in the past. I've avoided sword by adding a constant and then later subtracting as a displacement.

I use a related approach when communicating with a computer by multiplying by 100, sending it as an integer and then dividing by 100 to recover the decimal point. I think you have used a similar approach.

I use a simple bubble sort of values and choose the mean to get the steadier result rather than a simple arithmetic average.

We should put our joint WIKI article on using integers back on our new forum. Do you still have a copy?
John

John Drew

We were typing at the same time Tim.
John

top204

The scaling with multiplications and divisions is, really, only for very small fractional values, and you are right, multiplication of negative values is a strange querk of mathematics. For example, use the calculator and multiply -10 x -10... It equals +100?

When dealing with negative values inside trignometry or some other methematic functions, a flag is set for the sign of the initial variable. For example:

    Dim tNegPos As Bit = 0                  ' Default to a positive value
   
    If pValue < 0 Then                      ' Is pValue less than 0?
        tNegPos = 1                         ' Yes. So set bit tNegPos         
    EndIf
    pValue = Abs(pValue)                    ' Convert a negative value to a positive value
'
' Perform any calculations here for rounding etc... Into the "Result" variable
'
    If tNegPos = 1 Then                     ' Was pValue a negative value?
        Result = -Result                    ' Yes. So make it negative again
    EndIf

As can be seen, the flag is set to say the inital value held in pValue was negative, make it a positive value with Abs, and when things are complete, and if applicable, if the flag was set, make the result a negative value as well.

Strange, but part of mathematics. :-)

TimB


Hi,

What I think I will go with is simple scaling

I take my number + 100 to make it positive, then *10 to cover the first place

FOr example

-25.1  + 100 = 74.9
74.9 * 10 = 749.0

I can then just transpose that to a word = 749

To reverse it

Float1 = 749 / 10  (= 74.9)
Float1 - 100 (= -25.1)

I can make a simple Proc to do the conversion and plug it into my code with ease.

Tim

John, that was what you proposed, Thanks. BTW my memory is failing me I do not remember any tutorials on using integers.



TimB

#7
Some more thoughts

First off the ring buffer is not for averaging its for recording 10 seconds of data prior to the user needing the data recorded. So they can go back a bit in the record if they start the test/recording late.

I think I figured out what might have been the original issue was, although so much of it is internal timing I may be wrong.

I do have a ring buffer for averaging further down in the whole code. The image I posted of the graph was taken when I noticed the issue. What you notice is there was a very big drop in temperature just before the test started.
My code runs on ticks from a master clock eg it sets flags every x hz. So the code to do the temperature conversion might run at a different time to the code that saves the raw ADC to the 10 second ring buffer.
This may result in the value used to do the temperature conversion being different from the value saved to the buffer.

It could just be that the averaging ring buffer had a high value in it that was flush out on the next pass, just before I made the save.

Anyway that's the best I can come up with. Not going to be an issue now as I will use the same source for all the data conversion / saving now.

 
Another addition before my editing time runs out
One advantage I think I still have is that I can get the average of the array just be adding it all up and dividing it. All using integers and just at the last moment convert that back to a float