Shop OBEX P1 Docs P2 Docs Learn Events
What is in the Sine table? — Parallax Forums

What is in the Sine table?

ksltdksltd Posts: 163
edited 2015-02-23 10:10 in Propeller 1
I can find no information in the manual about the format of the 16-bit unsigned values in the sine table. I'm presuming they're fixed point values, but where's the implied binary point? Is the data 1.15?

Comments

  • Heater.Heater. Posts: 21,230
    edited 2014-08-16 22:35
    Do have a look in the fine Propeller manual, Appendix B Math Samples and Function Tables - Sine Table.

    http://www.parallax.com/downloads/propeller-manual
  • ksltdksltd Posts: 163
    edited 2014-08-17 08:14
    Heater,

    Thanks for your response. If you read my post carefully, you'll see that it says "I can find no information in the manual about the format of the 16-bit unsigned values in the sine table". That includes the samples in Appendix B.

    Neither the text nor the code snippet indicate the format of the data in the ROM and the code snippet doesn't indicate the format of the data that it returns.

    I'm sure you're trying to be helpful - but there's just nothing there.

    Perhaps someone who knows?
  • Martin_HMartin_H Posts: 4,051
    edited 2014-08-17 08:39
    It's been a few months since I last used the sine table, but if I recall correctly it contains one quadrant of sine values. Your achieve the full circle of values by mapping the other quadrants back to the first one and keeping track of the sign of the result. The index into the table is a binary radian of thirteen bits. The value is fixed point, but could be scaled into a standard floating point value. I used the binary radians and fixed point values directly, but I imagine a floating point angle could be scaled into a brad13 value and the result scaled into a float between 0 and 1.

    If you can read Forth, here are my fixed point trig functions:
    \ begin of fixed point trig functions.
    
    \ Values for common angles in brad13
    0016 constant DEGREE_ANGLE
    0400 constant EIGHTH_ANGLE
    0800 constant RIGHT_ANGLE
    1000 constant STRAIGHT_ANGLE
    
    \ sin function using brads ( brad13 -- sin32 )
    \ The sine table contains one quadrant, so we'll need to
    \ work around that when generating the index into the table.
    \ note use um* with the return value!
    : sin
        dup \ save a copy of input for later.
        dup RIGHT_ANGLE and
        if
            \ ranges from 800 to 001
            negate FFF and
        else
            \ ranges from 000 to 7FF
            7FF and
        then
    
        \ convert to word count and index into sin table
        2* E000 + W@
    
        \ bit shift to 32 bits for future um*
        F lshift
    
        \ use the sign from the preserved value.
        swap
        STRAIGHT_ANGLE and
        if 
            negate
        then
    ;
    
    decimal
    
    \ cos defined in terms of sin by coordinate rotation.
    : cos
        RIGHT_ANGLE +
        sin
    ;
    
    \ tangent can be derived from sine / cos. Both sine and cos are
    \ fast table lookups, so this is only a division more expensive
    \ than sine.
    \ ( brad13 -- tan )
    : tan
        dup
        sin
        swap cos
    
        \ Note that we shift the cos down because we only want the 
        \ numerator shifted left!
        15 asr/
        /
    ;
    
    RIGHT_ANGLE sin constant MAX_SIN
    RIGHT_ANGLE negate sin constant MIN_SIN
    
    \ clamps n between max and min to prevent inverse trig function overflow
    \ ( n -- clamped_value )
    : clamp
        MIN_SIN max MAX_SIN min
    ;
    
    \ compute asin via a binary search.
    \ Note: we're shifting angles up two bits to allow for greater
    \ precision during the search. It will be shifted back before return.
    \ ( sin - brad13 )
    : asin
        clamp
        \ push the first approximation and correction
        0 EIGHTH_ANGLE 4*
    
        \ refine approximation and correction iteratively
        13 0 do
            \ move the correction to the bottom of stack.
            -rot
    
            \ compare the sine of the approximation to the value.
            2dup 2/ 2/ sin >
    
            if
                \ The approximation is too small, add the correction.
                rot dup -rot +
            else
                \ The approximation is too large, so decrease it.
                rot dup -rot -
            then
    
            \ half the correction factor for next iterration.
            swap 2/
        loop
    
        \ return only the approximation.
        drop swap drop 2/ 2/
    ;
    
    \ acos is defined in terms of asin
    \ ( cos -- brad13 )
    : acos
        asin RIGHT_ANGLE - negate
    ;
    
    \ 13-bit fixed point four-quadrant arctangent. Given Cartesian vector (x, y),
    \ finds the angle subtended by the vector and the positive x-axis.
    \ y a signed integer
    \ x a signed integer
    \ ( y x -- brad13 )
    : atan2
        \ Handle crossing x axis case
        dup 0= if
            drop
            RIGHT_ANGLE
            swap
            \ adjust sign based upon y's sign.
            0< if
                negate
            then
            exit
        then
    
        \ save x to use its sign to adjust results
        dup -rot
    
        swap 15 lshift swap /
    
        \ push the first approximation and correction
        0 EIGHTH_ANGLE 4*
    
        \ refine approximation and correction iteratively
        13 0 do
            \ move the correction to the bottom of stack.
            -rot
    
            \ compare the sine of the approximation to the value.
            2dup 2/ 2/ tan >
    
            if
                \ The approximation is too small, add the correction.
                rot dup -rot +
            else
                \ The approximation is too large, so decrease it.
                rot dup -rot -
            then
    
            \ half the correction factor for next iterration.
            swap 2/
        loop
    
        \ return only the approximation.
        drop swap drop 2/ 2/
    
        \ if x's sign was negative, then move results into 3 & 4 quadrants.
        swap 0< if
            STRAIGHT_ANGLE +
        then
    ;
    
    \ end of fixed point trig functions.
    
  • Heater.Heater. Posts: 21,230
    edited 2014-08-17 08:47
    On page 34 of the manual we have:

    Sine Table

    The sine table provides 2,049 unsigned 16-bit sine samples spanning from 0° to 90°,

    inclusively (0.0439° resolution). Sine values for all other quadrants covering > 90° to < 360°
    can be calculated from simple transformations on this single-quadrant sine table. The sine
    table can be used for calculations related to angular phenomena.
    See Appendix B: Math Samples and Function Tables on page 380 for more information.

    Th appendix says the same and has an example of how to extend that too the full 360 degrees.

    I'm going to assume the value at position 0 in the table is 00000000_00000000 seeing as sine(0) = 1.

    I'm going to assume the value at position 2048 is 11111111_11111111 as that is the biggest unsigned 16 bit value one can have. It must represent 1.0 as sine(90) = 1.

    So effectively the binary point is in front of that 0.11111111_11111111

    The right most bit, the LSB, therefore being 90 / 2048 = 0.0439 degrees as stated in the manual.

    The procedure presented in the index will get you the full circle.

    I might be wrong but a quick Spin program would verify it. All the info required seems to be there.
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-08-17 13:07
    Wayne,

    Think of it as the values in the table as 16 bit unsigned integers. The output values represent fractional sine values ranging from 0 to 65535/65536 for values in the first quadrant. Effectively fixed point radix to the left of the 16th bit.

    Here is a Spin program that prints out values at intervals around the full circle. The FullSine method takes an angle\ from 0 to 8191 and returns the sine of that angle as an integer from -65535 to +65535. Those values represent sines from -65535/65536 to +65535/65536. The program also converts the result into decimal, 4 digits precision.
    [FONT=courier new]
    [/FONT][SIZE=1][FONT=courier new]CON
      _CLKMODE = XTAL1 + PLL16X
      _XINFREQ = 5_000_000
    VAR
    OBJ
      pst : "parallax serial terminal"
    
    PUB basicSine | angle, sine
      pst.start(9600)
      waitcnt(clkfreq/10+cnt)
      pst.str(string("top",13))
      repeat angle from 0 to 8192 step 256  ' step through the angles represnting circle, 360 degrees
        sine:=fullsine0(angle)   ' this does the lookup in the hub sine table and adjust for quadrant
    
        ' show results...
        pst.dec(angle)
        pst.chars(32,5)
        pst.dec(angle>>11)   ' quadrant
        pst.chars(32,5)
        pst.dec(sine)         ' returned sine value, range -65535 to 65535    pst.chars(32,5)
        sine := 20000 ** (sine << 15)  ' convert to decimal, 4 digits
        pst.chars(32,5)
        pst.dec(sine/10000)
        pst.char(".")
        pst.dec(sine//10000)
        pst.char(13)
        waitcnt(clkfreq/20+cnt)
      repeat
    
    
    
    PUB fullsine0(angle) | q                                ' angle is 13 bits, 0 to 360 degrees
      q := angle >> 11                                      ' quadrant, 0 to 3
      angle := (angle & $7ff) << 1                          ' 0 to 90- degrees, adjust to 11 bits
      case q                                                ' by quadrant, shift for word address
        0 : result := word[$E000 + angle]                       '
        1 : result := word[$F000 - angle]
        2 : result := -word[$E000 + angle]
        3 : result := -word[$F000 - angle]
      return[/FONT][/SIZE]
    
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-08-17 18:16
    Heater, sorry, I managed inadvertently to delete your post that pointed out my poor wording in post #6, which I've edited. I hope it's better now.
  • ksltdksltd Posts: 163
    edited 2014-08-17 22:39
    Martin,

    It's definitely not indexed by radians, it's indexed by 1/2048ths of degrees. That much is explained in the manual.

    Heater,

    If all the information were there, believe me, I'd not have asked the question. And, I'd imagine that your explanation wouldn't be couched in two assumptions and the ubiquitous "I might be wrong".

    Tracy,

    So you're saying values in the table are in 0.16 fixed point format and your program takes Angles in degrees with the LSB equal to 90/2048 degrees and only works for angles in the range 0..360. Got it. Thanks.
  • Cluso99Cluso99 Posts: 18,069
    edited 2014-08-18 02:01
    The code that built the ROM is published on this thread...
    http://forums.parallax.com/showthread.php/156866-Question-for-Chip-re-ROM-code
  • Heater.Heater. Posts: 21,230
    edited 2014-08-18 02:35
    ksltd,
    It's definitely not indexed by radians, it's indexed by 1/2048ths of degrees. That much is explained in the manual.
    It is definitely NOT indexed by "1/2048ths of degrees".

    Radians or degrees it makes on difference.

    What we have here is 2049 numbers representing the value of the sine function over one quarter of a full rotation.

    What the manual clearly states is "...2,049 unsigned 16-bit sine samples spanning from 0° to 90°,
    inclusively (0.0439° resolution)".

    One could just as well say "...2,049 unsigned 16-bit sine samples spanning from 0 to PI/2 radians,
    inclusively (0.0007666 radian resolution)".

    Same thing.

    We see that all the information is indeed in the manual.

    Yes I'm fond of the "I might me wrong". Perhaps we find that the manual has errors or omissions on this point, would not be the first time. Perhaps the ROM in the Propeller is wrong, unlikely. Perhaps my own understanding or arithmetic has slipped up, that certainly would not be a first.

    Finally given the information in the manual, which turns out to be correct and complete, it would only take a five minute experiment to dump out the values and check ones understanding.
  • Martin_HMartin_H Posts: 4,051
    edited 2014-08-18 03:45
    ksltd wrote: »
    Martin,

    It's definitely not indexed by radians, it's indexed by 1/2048ths of degrees. That much is explained in the manual.

    I said binary radian, not radian. It's a common technique for representing trig functions with integers. See this article:

    http://en.wikipedia.org/wiki/Binary_scaling
  • ErNaErNa Posts: 1,752
    edited 2014-08-18 07:47
    I'm working with the propeller now for years. And still there is more knowledge in the propeller documentation than in my brain. So to me it is easy to learn something new be repeatedly read another chapter in the propeller data book. And I feel happy like a child, if I find some information, cleverly hidden behind a bush. ;-)
  • Tracy AllenTracy Allen Posts: 6,664
    edited 2014-08-19 12:26
    Tracy,
    So you're saying values in the table are in 0.16 fixed point format and your program takes Angles in degrees with the LSB equal to 90/2048 degrees and only works for angles in the range 0..360. Got it. Thanks.

    Sort of. Implementation of trig functions always involves clever folding, scaling and range reduction. My snippet of a program does the calculation in brads (as Martin_H said, binary radians) with the full circle broken into 8192 segments. It is easy enough to convert that to units of degrees or radians if that is what is required. Range extension too is easy, if required. The 11 bit precision going in is less than the 16 bit precision coming out, so the 0.16 fixed point format is a bit deceptive unless you also implement interpolation, as is done in the floating point packages. I can see it as 0.16 fixed point, but I think of it first as a dimensionless ratio of integers apart from any particular number system or scale factor.


    Erna
    And I feel happy like a child, if I find some information, cleverly hidden behind a bush. ;-)
    A poet as always!
  • KyeKye Posts: 2,200
    edited 2014-08-19 13:16
    http://obex.parallax.com/object/609

    Here's an object I wrote that uses the Sin table in Spin.

    Most functions take a radius argument. The radius argument is necessary to scale the sin value output to be an integer number. Otherwise a value of -1 to 1 would be returned. Generally, just call the functions with the radius value set to something large. Like 100 or 1000.
  • edited 2015-01-02 06:53
    Heater. wrote: »
    What we have here is 2049 numbers representing the value of the sine function over one quarter of a full rotation.

    2049 sounds like an odd value to me. Is it a typo?

    Sandy
  • Heater.Heater. Posts: 21,230
    edited 2015-01-02 07:42
    It is a bit of a weird number. But see my quote from the manual in post #5 above. I believe it is correct.

    Imagine you wanted to plot a graph of some function, y = f(x), over the range x >= 0, x >= 10. And you only had values of y for integer values of x, x = 0,1,2,3,4,5,6,7,8,9,10

    See, we have 11 points to plot not just 10. This is a "fence post" problem. There is one more fence post that the number of gaps between the posts to cover a particular length of fence.

    So it goes with the table of sin(x) in the PROM.
  • edited 2015-01-02 23:10
    Heater. wrote: »
    It is a bit of a weird number. But see my quote from the manual in post #5 above. I believe it is correct.

    I think you're right.

    I got bored and wrote a little program to dump the contents of the sine table. The results are included as Sine Table Dump.txt in the archive. I had to do in in three goes because otherwise the results would be clipped in the debug window. The debug window contents were copied into Sine Table Dump.txt after each program run.

    After word number 2048 ( address F000 to F001 ) the numbers are obviously wrong.


    Sandy
  • ozpropdevozpropdev Posts: 2,792
    edited 2015-01-03 00:48
    2049 Entries is correct.
    The sine table addresses are from $E000 to $F001 and the booter/interpreter addresses are $F002 to $FFFF.
    See page 31 in the Propeller manual (Memory map) :)
  • LoopyBytelooseLoopyByteloose Posts: 12,537
    edited 2015-01-03 05:01
    ozpropdev wrote: »
    2049 Entries is correct.
    The sine table addresses are from $E000 to $F001 and the booter/interpreter addresses are $F002 to $FFFF.
    See page 31 in the Propeller manual (Memory map) :)

    Yes, this is indeed correct and a special accommodation of 2049 entries in order to make the use of binary radians deploy easily. At some point, the use of the table requires one to learn what a 'binary radian' is and how to use it with binary maths. The Table is intended to allow optimal programing in binary maths, not floating point or fixed point.

    Search on BRADs...
    http://en.wikipedia.org/wiki/Binary_scaling
  • tomcrawfordtomcrawford Posts: 1,126
    edited 2015-02-23 10:10
    Kye wrote: »
    http://obex.parallax.com/object/609

    Here's an object I wrote that uses the Sin table in Spin.

    Most functions take a radius argument. The radius argument is necessary to scale the sin value output to be an integer number. Otherwise a value of -1 to 1 would be returned. Generally, just call the functions with the radius value set to something large. Like 100 or 1000.

    Which works perfectly. I just cut and pasted two PUBs (Sin and Cos) and two PRI helper methods (one scales the radius and one does the actual lookup) and LoAndBehold: an analog clock!
Sign In or Register to comment.