Shop OBEX P1 Docs P2 Docs Learn Events
My Attempt to Understand the Sine Table — Parallax Forums

My Attempt to Understand the Sine Table

Duane DegnDuane Degn Posts: 10,588
edited 2015-09-07 20:11 in Propeller 1
This has been on my todo list for a while. I've wanted to understand how to use the sine table in the Propeller's ROM for several years but I never had a need for it in any of my projects. I finally took some time to see how it works.

Appendix B of the Propeller Manual explains the table well and gives and example of how to use the table from PASM.

I wanted to make sure I understood how the table worked so I make a Spin program to display the sine values of the angles from zero to ninety degrees.

Here's part of the output:
Press any key to start program.
word[$E000][$000] = $000 or word[57_344][0] = 0 | sin( 0.0000) =  0.0000
Press to continue.
word[$E000][$010] = $324 or word[57_344][16] = 804 | sin( 0.7031) =  0.0122
word[$E000][$020] = $648 or word[57_344][32] = 1608 | sin( 1.4062) =  0.0245
word[$E000][$030] = $96C or word[57_344][48] = 2412 | sin( 2.1093) =  0.0368
word[$E000][$040] = $C90 or word[57_344][64] = 3216 | sin( 2.8125) =  0.0490
word[$E000][$050] = $FB3 or word[57_344][80] = 4019 | sin( 3.5156) =  0.0613
word[$E000][$060] = $2D5 or word[57_344][96] = 4821 | sin( 4.2187) =  0.0735
word[$E000][$070] = $5F7 or word[57_344][112] = 5623 | sin( 4.9218) =  0.0858
word[$E000][$080] = $918 or word[57_344][128] = 6424 | sin( 5.6250) =  0.0980
word[$E000][$090] = $C37 or word[57_344][144] = 7223 | sin( 6.3281) =  0.1102
word[$E000][$0A0] = $F56 or word[57_344][160] = 8022 | sin( 7.0312) =  0.1224
word[$E000][$0B0] = $274 or word[57_344][176] = 8820 | sin( 7.7343) =  0.1345
word[$E000][$0C0] = $590 or word[57_344][192] = 9616 | sin( 8.4375) =  0.1467
word[$E000][$0D0] = $8AB or word[57_344][208] = 10411 | sin( 9.1406) =  0.1588
word[$E000][$0E0] = $BC4 or word[57_344][224] = 11204 | sin( 9.8437) =  0.1709
word[$E000][$0F0] = $EDC or word[57_344][240] = 11996 | sin(10.5468) =  0.1830
<snip>
word[$E000][$270] = $5E5 or word[57_344][624] = 30181 | sin(27.4218) =  0.4605
word[$E000][$280] = $8AD or word[57_344][640] = 30893 | sin(28.1250) =  0.4713
word[$E000][$290] = $B70 or word[57_344][656] = 31600 | sin(28.8281) =  0.4821
word[$E000][$2A0] = $E2E or word[57_344][672] = 32302 | sin(29.5312) =  0.4928
word[$E000][$2B0] = $0E7 or word[57_344][688] = 32999 | sin(30.2343) =  0.5035
word[$E000][$2C0] = $39C or word[57_344][704] = 33692 | sin(30.9375) =  0.5141
word[$E000][$2D0] = $64B or word[57_344][720] = 34379 | sin(31.6406) =  0.5245
word[$E000][$2E0] = $8F5 or word[57_344][736] = 35061 | sin(32.3437) =  0.5349
<snip>
word[$E000][$3E0] = $085 or word[57_344][992] = 45189 | sin(43.5937) =  0.6895
word[$E000][$3F0] = $2C8 or word[57_344][1008] = 45768 | sin(44.2968) =  0.6983
word[$E000][$400] = $504 or word[57_344][1024] = 46340 | sin(45.0000) =  0.7071
Press to continue.
word[$E000][$410] = $739 or word[57_344][1040] = 46905 | sin(45.7031) =  0.7157
word[$E000][$420] = $968 or word[57_344][1056] = 47464 | sin(46.4062) =  0.7242
<snip>
word[$E000][$780] = $EC3 or word[57_344][1920] = 65219 | sin(84.3750) =  0.9951
word[$E000][$790] = $F0D or word[57_344][1936] = 65293 | sin(85.0781) =  0.9963
word[$E000][$7A0] = $F4D or word[57_344][1952] = 65357 | sin(85.7812) =  0.9972
word[$E000][$7B0] = $F84 or word[57_344][1968] = 65412 | sin(86.4843) =  0.9981
word[$E000][$7C0] = $FB0 or word[57_344][1984] = 65456 | sin(87.1875) =  0.9987
word[$E000][$7D0] = $FD3 or word[57_344][2000] = 65491 | sin(87.8906) =  0.9993
word[$E000][$7E0] = $FEB or word[57_344][2016] = 65515 | sin(88.5937) =  0.9996
word[$E000][$7F0] = $FFA or word[57_344][2032] = 65530 | sin(89.2968) =  0.9999
word[$E000][$800] = $FFF or word[57_344][2048] = 65535 | sin(90.0000) =  1.0000

The first section of each line shows the ROM location being read and the raw 16-bit value at this location.

The final section of each line translates these values to more easily understood numbers. These final values show the angle (between 0 and 90 degrees) represented by the 16-bit location index and the corresponding 16-bit sine with the scale changed from 0 through 65,535 to 0 through 1.

Only integer math was used. The final result was scaled by a factor of 10,000 and displayed with a decimal point inserted in the correct location.

The program displays 32 lines and waits for a keypress to continue. I step the index by 16 so only 1/16 of the table is displayed (it's a big table).

Here's the program:
CON

  _clkmode = xtal1 + pll16x                           
  _xinfreq = 5_000_000

  SCALED_MULTIPLIER = 10_000
  SCALED_DECIMAL_PLACES = 4
  SCALED_NINETY_DEGREES = 90 * SCALED_MULTIPLIER
  SIZE_OF_SINE_TABLE = 2_049 ' From Propeller Manual
  MAX_TABLE_INDEX = SIZE_OF_SINE_TABLE - 1
  ONE_AS_16_BITS = $FFFF ' sin(90) = $FFFF or 1
  SINE_TABLE_LOCATION = $E000
  
OBJ

  Pst : "Parallax Serial Terminal"
  Format : "StrFmt"
   
PUB Setup

  Pst.Start(115_200)
 
  repeat
    result := Pst.RxCount
    Pst.Str(string(11, 13, "Press any key to start program."))
    waitcnt(clkfreq / 2 + cnt)
  until result
  
  Pst.RxFlush

  TestMath 

PUB TestMath | sineIndex
 
  repeat sineIndex from 0 to MAX_TABLE_INDEX step $10
    Pst.Str(string(11, 13, "word[$E000][$")) ' get the raw values in hex and dec
    Pst.Hex(sineIndex, 3)
    Pst.Str(string("] = $"))
    Pst.Hex(word[SINE_TABLE_LOCATION][sineIndex], 3)
    Pst.Str(string(" or word[57_344]["))
    Pst.Dec(sineIndex)
    Pst.Str(string("] = "))
    Pst.Dec(word[SINE_TABLE_LOCATION][sineIndex])

    '' convert raw values to properly scaled values
    Pst.Str(string(" | sin("))      
    DecPoint(sineIndex * SCALED_NINETY_DEGREES / MAX_TABLE_INDEX, SCALED_DECIMAL_PLACES)
    Pst.Str(string(") = "))
    DecPoint(word[SINE_TABLE_LOCATION][sineIndex] * SCALED_MULTIPLIER / ONE_AS_16_BITS, {
    } SCALED_DECIMAL_PLACES)
    
    ifnot sineIndex // $200 
      PressToContinue
  
  repeat ' Keep cog alive

PUB PressToContinue
  
  Pst.Str(string(11, 13, "Press to continue."))
  repeat
    result := Pst.RxCount
  until result
  
  Pst.RxFlush

PUB DecPoint(value, decimalPlaces) | localBuffer[4]

  result := Format.FDec(@localBuffer, value, decimalPlaces + 3, decimalPlaces)
  byte[result] := 0
  Pst.str(@localBuffer)

I'm sure there are many examples of accessing the sine table around and now there is one more.

I tried to keep the program simple. It doesn't do any of the tricks to compute the sine of angles other than 0 to 90 degrees and it doesn't attempt to compute the cosine of angles.

If any of you already familiar with how to use the sine table have a few minutes to spare, I'd appreciate your taking a look at the code to make sure I'm using the correct constants in my calculations. The results appear correct but I think they may also appear correct if I were a bit off (double meaning).

An archived version of the program is available as "MathTestSine" in my CncController GitHub repository.

Edit (September 7, 2015): I attached the archive to this post.

Comments

  • Mark_TMark_T Posts: 1,981
    edited 2015-05-10 12:26
    That's correct, offset 0 represents zero angle, (byte)offset $1000 represents pi/2, the values are 0..$FFFF representing
    0.0 to 1.0
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-05-10 18:54
    Actually the sine table is a rare example where there is actually an extra point. The sine table is located at hub rom $E000..$F001 which is 2049 words (not 2048 words).
    Its like using 0-10 instead of the usual 0-9 because you need entries for both 0 degrees and 90 degrees.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-05-10 20:02
    Cluso99 wrote: »
    Actually the sine table is a rare example where there is actually an extra point. The sine table is located at hub rom $E000..$F001 which is 2049 words (not 2048 words).

    I think I took this into account in my code. With 2049 elements, the table would be indexed from 0 to 2048 right? With the index # 0 corresponding to 0 degrees and index # 2048 corresponding to 90 degrees. Right?

    Were you just pointing out a possible "gotcha" to watch out for or were you pointing out an error in my program? I'm figuring it was the former so if I made a mistake please let me know.
  • Heater.Heater. Posts: 21,230
    edited 2015-05-10 20:27
    I never quite understood the reason for using 2049 words instead of 2048.

    Working in degrees:

    sin(90) = 1

    The table entry below that must be about:

    sin(90 - 90 / 2048) = 0.9999997058628822

    The difference between those being:

    2.9413711777337426e-7

    or about:

    0.0000003

    That difference is below 16 bit resolution of the table entries. So surely the last two entries of the table are the same. We don't need the 2049th entry.

    What am I missing here?
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-05-10 22:02
    Just pointing out its 2049 entries.

    Heater: that's true the last few entries are indeed ffff but then your program would need to check for this, so it's more complex and uses more code. Easier to make it 2049 entries being 0..2048.

    The rom had space, but it meant the interpreter starts at $F004. There are a few sets spare in the rom, enough for me to split the runner into 3 parts, giving me 1KB of contiguous space for my interpreter 256 * long vector table in the P1V :)
  • Duane DegnDuane Degn Posts: 10,588
    edited 2015-05-10 22:33
    Heater. wrote: »
    We don't need the 2049th entry.

    I suppose it could be argued we don't need the zeroth entry either but IMO, the table as it is makes the most intuitive sense.
    The sine table provides 2,049 unsigned 16-bit sine samples spanning from 0 degrees to 90 degrees, inclusively (0.0439 degree resolution).

    After reading this single sentence in the appendix about the sine table, I had a pretty good guess how it worked. I was pleasantly surprised how easy it was to convert between a table index and its corresponding degree value. Converting the 16-bit value to a scaled integer was also simple.

    I personally think the sine table is just the way it should be.
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-05-11 00:44
    Duane Degn wrote: »
    After reading this single sentence in the appendix about the sine table, I had a pretty good guess how it worked. I was pleasantly surprised how easy it was to convert between a table index and its corresponding degree value. Converting the 16-bit value to a scaled integer was also simple.

    I personally think the sine table is just the way it should be.
    Totally agree with you :)
  • Heater.Heater. Posts: 21,230
    edited 2015-05-11 04:17
    I'm sure the table is fine. Just a bit odd. I guess having that extra entry saves an instruction or two here and there when using it.
Sign In or Register to comment.