An afterthought, before the fact.
I went through this trying to make sure it didn't require PBP3 since Rob doesn't have it. But now that I've posted it, I realized it requires PBP 2.60 or later. DOH!
At least you can download the free PBP3 trial version and compile it with that, or you can change the arrayread/arraywrite statements, so I'm not changing it.
---------------------------------- -------------------------------- ----------------------------------
Ok, here goes ...
It's not as ugly as it was, but without PBP3 there's only so much you can do.
This code is based on the Perl script on the Wikipedia site for Maidenhead Locator System by Chris Ruvolo.
Although I had to do things quite differently for the Microchip Floating-Point routines.
The Microchip Floating-Point routines for PBP are available from http://melabs.com/resources/fp.htm
Extract the .zip file to the same folder as your source code, or this code.
To try to make things easy, the routines start out with a String in the same format as the Lat/Lon data of the $GPRMC sentence.
For instance, an approximation of the IO93ok square is 0 degrees, 47.70 minutes West Longitude by 53 degrees, 26.20 minutes North latitude.
$GPRMC will present the data as .... 5326.20,N and 00047.70,W.
These can be parsed from the GPS data, or entered manually with the following two statements ... Note that they are null terminated.
Code:
ARRAYWRITE LonStr,["00047.70,W",0] ; -0° 47.70'
ARRAYWRITE LatStr,["5326.20,N",0] ; 53° 26.20
I am entering them manually for this sample.
I'm using the 24-bit Floating-Point routines, since it gave the same results as the 32-bit FP, but you can use either one by simply changing the include file.
I imagine the 32-bit routines will be better around the edges of each square, but I haven't confirmed that.
I'm using a 16F1937, so if you are using an 18F ... you'll need to use the 18F FP includes.
Obviously, you need to initialize your hardware configs first.
Code:
; Filename : Maidenhead.pbp
; Compiler : PICBASIC PRO Compiler
; Target PIC : 16F1937, but will work with most any chip
; Oscillator : ANY
; Keywords : Maidenhead, Locator, GPS, Amateur Radio
; Description : PICBASIC PRO program to convert between
; : GPS Sexagesimal to Maidenhead coordinate systems
;-------------------------------------------------------------------------------
; use only one of the FP includes
INCLUDE "FP20.bas" ; 24-bit Floating Point for 14-bit cores with RAM at $20
;INCLUDE "FP2032.bas" ; 32-bit Floating Point for 14-bit cores with RAM at $20
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 20h ' Enable transmit, BRGH = 0
DEFINE HSER_SPBRG 25 ' 19200 Baud @ 32MHz, 0.16%
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
HSEROUT [27,"[2J","Maidenhead Locator",13,10]
HSEROUT ["16F1937, 32Mhz, PBP3 not required",13,10,13,10]
MH VAR BYTE[6]
LonStr VAR BYTE[12]
LatStr VAR BYTE[12]
LonDeg VAR BYTE
LonMin VAR BYTE
LonMinDec VAR BYTE
LonDir VAR BYTE
LatDeg VAR BYTE
LatMin VAR BYTE
LatMinDec VAR BYTE
LatDir VAR BYTE
ARRAYWRITE LonStr,["00047.70,W",0] ; -0° 47.70' <-- Enter location here
ARRAYWRITE LatStr,["5326.20,N",0] ; 53° 26.20'
HSEROUT ["LonStr = ", STR LonStr,13,10] ; display manual input
HSEROUT ["LatStr = ", STR LatStr,13,10]
;----[parse strings as if they came from a GPS]---------------------------------
ARRAYREAD LonStr,[DEC3 LonDeg, DEC2 LonMin, Dec LonMinDec, LonDir]
ARRAYREAD LatStr,[DEC2 LatDeg, DEC2 LatMin, Dec LatMinDec, LatDir]
;----[convert Longitude Sexagesimal to Decimal Degrees]-------------------------
Aint = LonMinDec : GOSUB ItoFA ; decimal portion of Minutes
Bint = 100 : GOSUB ItoFB ; divided by 100
GOSUB fpdiv
Bint = LonMin : GOSUB ItoFB ; add whole portion of Minutes
GOSUB fpadd
Bint = 60 : GOSUB ItoFB ; divide by 60
GOSUB fpdiv
Bint = LonDeg : GOSUB ItoFB ; add degrees
GOSUB fpadd
IF LonDir = "W" THEN ; if west of Prime Meridian
GOSUB AtoB
Aint = 0 : GOSUB ItoFA ; subtract from 0 to negate
GOSUB fpsub
ENDIF
Bint = 180 : GOSUB ItoFB ; add 180
GOSUB fpadd
;----[Have Longitude in AARG as Float]------------------------------------------
GOSUB SaveLon ; save Longitude for later
Bint = 20 : GOSUB ItoFB ; Lon / 20
GOSUB fpdiv
GOSUB SaveAARG ; save AARG for modulus
GOSUB FtoIA ; get integer
MH(0) = "A" + Aint ; + "A" = First character
; -- do Lon // 20 --- (modulus)
Bint = Aint ; copy integer result to BARG
GOSUB ItoFB ; convert it to float
GOSUB RestoreAARG ; restore Lon / 20 to AARG
GOSUB fpsub ; subtract integer
Bint = 20 ; multiply times original divisor (20)
GOSUB ItoFB
GOSUB fpmul ; AARG now contains the modulus
Bint = 2 : GOSUB ItoFB ; divide modulus by 2
GOSUB fpdiv
GOSUB FtoIA ; get integer
MH(2) = "0" + Aint ; + "0" = Third character
GOSUB RestoreLon ; restore Longitude
Bint = 2 : GOSUB ItoFB
GOSUB fpdiv ; divide by 2
GOSUB FtoIA ; get integer
Bint = Aint * 2 : GOSUB ItoFB ; put int * 2 in BARG, change back to float
GOSUB RestoreLon ; restore original Longitude
GOSUB fpsub ; subtract A - B
Bint = 12 : GOSUB ItoFB ; Multiply * 12
GOSUB fpmul
GOSUB FtoIA ; get integer
MH(4) = "a" + Aint ; + "a" = Fifth Character
;----[convert Latitude Sexagesimal to Decimal Degrees]--------------------------
Aint = LatMinDec : GOSUB ItoFA ; decimal portion of Minutes
Bint = 100 : GOSUB ItoFB ; divided by 100
GOSUB fpdiv
Bint = LatMin : GOSUB ItoFB ; add whole portion of Minutes
GOSUB fpadd
Bint = 60 : GOSUB ItoFB ; divide by 60
GOSUB fpdiv
Bint = LatDeg : GOSUB ItoFB ; add degrees
GOSUB fpadd
IF LatDir = "S" THEN ; if south of equator
GOSUB AtoB
Aint = 0 : GOSUB ItoFA ; subtract from 0 to negate
GOSUB fpsub
ENDIF
Bint = 90 : GOSUB ItoFB ; add 90
GOSUB fpadd
;----[Have Latitude in AARG as Float]------------------------------------------
GOSUB SaveLon ; save Latitude for later
Bint = 10 : GOSUB ItoFB ; Lat / 10
GOSUB fpdiv
GOSUB SaveAARG ; save AARG for modulus
GOSUB FtoIA
MH(1) = "A" + Aint ; + "A" = Second character
; -- do Lat // 10 --- (modulus)
Bint = Aint ; copy integer result to BARG
GOSUB ItoFB ; convert it to float
GOSUB RestoreAARG ; restore Lat / 10 to AARG
GOSUB fpsub ; subtract integer
Bint = 10 ; multiply times original divisor (10)
GOSUB ItoFB
GOSUB fpmul ; AARG now contains the modulus
GOSUB FtoIA
MH(3) = "0" + Aint ; + "0" = Fourth character
GOSUB RestoreLon ; restore original Latitude
GOSUB FtoIA ; get integer
Bint = Aint : GOSUB ItoFB ; put int in BARG, change back to float
GOSUB RestoreLon ; restore original Latitude
GOSUB fpsub ; subtract A - B
Bint = 24 : GOSUB ItoFB ; Multiply * 24
GOSUB fpmul
GOSUB FtoIA ; get integer
MH(5) = "a" + Aint ; + "a" = Sixth character
;----[Maidenhead conversion complete]-------------------------------------------
HSEROUT ["LOC ",STR MH\6,13,10,13,10] ; show final result
Main: ; blink an LED when done
HIGH PORTA.0
PAUSE 500
LOW PORTA.0
PAUSE 500
GOTO Main
;-------------------------------------------------------------------------------
Aexp_Copy VAR BYTE
AARGB0_Copy VAR BYTE
AARGB1_Copy VAR BYTE
AARGB2_Copy VAR BYTE
Aexp_Lon VAR BYTE
AARGB0_Lon VAR BYTE
AARGB1_Lon VAR BYTE
AARGB2_Lon VAR BYTE
SaveAARG:
Aexp_Copy = Aexp
AARGB0_Copy = AARGB0
AARGB1_Copy = AARGB1
AARGB2_Copy = AARGB2
RETURN
RestoreAARG:
Aexp = Aexp_Copy
AARGB0 = AARGB0_Copy
AARGB1 = AARGB1_Copy
AARGB2 = AARGB2_Copy
RETURN
AtoB:
Bexp = Aexp
BARGB0 = AARGB0
BARGB1 = AARGB1
BARGB2 = AARGB2
RETURN
SaveLon:
Aexp_Lon = Aexp
AARGB0_Lon = AARGB0
AARGB1_Lon = AARGB1
AARGB2_Lon = AARGB2
RETURN
RestoreLon:
Aexp = Aexp_Lon
AARGB0 = AARGB0_Lon
AARGB1 = AARGB1_Lon
AARGB2 = AARGB2_Lon
RETURN
The output from the program in a terminal looks like this ...
Code:
Maidenhead Locator
16F1937, 32Mhz, PBP3 not required
LonStr = 00047.70,W
LatStr = 5326.20,N
LOC IO93ok
If you are using an Enhanced 14-bit core like the 16F1937 that I'm using ... you'll need to add the processor to the DEV_FAM.INC file from the FP.zip
For the 16F1937 add this towards the bottom of the file (before the if statement).
Code:
IFDEF __16F1937 ; Generic Processor Type
P16CXX SET TRUE ; If P16CXX, use INHX8M file format.
P16_MAP2 SET TRUE ;
ENDIF
References:
Bookmarks