I've been searching and searching and my Google-fu has failed me. I'm trying to convert an encoded number from base-32 to decimal using either expressions or a macro, but I'm not finding anything. I know Excel has the "Decimal" function, I've been hoping that I could stumble onto something similar.
I'm reluctant to use VBA as I don't want to spend time re-learning the language right now and I'm worried that my organization will flag it as potentially dangerous (which could kill my attempts at making any databases).
With an input of "16O9E55"
I expect a result of 1300543653.
I should clarify that this is "base32hex" according to Wikipedia. It's 0-9, A-V. It's only 7 characters of base-32 that needs to convert to 10 digits of decimal. My use case is decoding a barcode into the data I need.
I doubt this can be accomplished without VBA. Consider code adapted from https://www.excelbanter.com/excel-worksheet-functions/150198-formulat-convert-base-32-decimal.html
Public Function Base32ToDec(Num As String) As Variant
Static Digits As String
Dim i As Integer
Dim myIndex As Integer
Dim myStr As String
Digits = "0123456789ABCDEFGHIJKLMNOPQRSTUV"
For i = Len(Num) To 1 Step -1
myStr = Mid(Num, i, 1)
myIndex = InStr(Digits, myStr) - 1
Base32ToDec = Base32ToDec + myIndex * 32 ^ (Len(Num) - i)
Next i
End Function
According to Wikipedia, Base32 uses a 32-character set comprising the twenty-six upper-case letters A–Z, and the digits 2–7. The variant base32hex starts with 0 - 9 and uses the letters A to V.
If only numbers with a limited range have been encoded, you can decode them easily with VBA, otherwise you would have to return an array of bytes and process it further.
You write, that you have up to 10 decimal digits. The question is, what the maximum number is. The Long type can store numbers up to 2,147,483,647. This are ten digits; however, with 10 digits you could store a number as big as 9,999,999,999.
Therefore, the following function returns the number as Double. If you know that your number will never exceed 2,147,483,647, then you can exchange the Double type by Long for the sum variable and the function return type.
Public Function DecodeBase32hex(ByVal encoded As String) As Double
Dim ch As String
Dim sum As Double
Dim d As Long, i As Long
For i = 1 To Len(encoded)
ch = Mid$(encoded, i, 1)
If ch >= "A" And ch <= "Z" Then
d = Asc(ch) - Asc("A") + 10
ElseIf ch >= "0" And ch <= "9" Then
d = Asc(ch) - Asc("0")
Else
Exit For 'E.g. padding charachters
End If
sum = 32 * sum + d
Next i
DecodeBase32hex = sum
End Function
Test in Access' immediate window:
?DecodeBase32hex("16O9E55")
1300543653
Related
In MS Access I have a table with a Short Text field named txtPMTaskDesc in which some records contains numbers, and if they do, at different positions in the string. I would like to recover these numbers from the text string if possible for sorting purposes.
There are over 26000 records in the table, so I would rather handle it in a query over using VBA loops etc.
Sample Data
While the end goal is to recover the whole number, I was going to start with just identifying the position of the first numerical value in the string. I have tried a few things to no avail like:
InStr(1,[txtPMTaskDesc],"*[0-9]*")
Once I get that, I was going to use it as a part of a Mid() function to pull out it and the character next to it like below. (its a bit dodgy, but there is never more than a two-digit number in the text string)
IIf(InStr(1,[txtPMTaskDesc],"*[0-9]*")>0,Mid([txtPMTaskDesc],InStr(1,[txtPMTaskDesc],"*[0-9]*"),2)*1,0)
Any assistance appreciated.
If data is truly representative and number always preceded by "- No ", then expression in query can be like:
Val(Mid(txtPMTaskDesc, InStr(txtPMTaskDesc, "- No ") + 5))
If there is no match, a 0 will return, however, if field is null, the expression will error.
If string does not have consistent pattern (numbers always in same position or preceded by some distinct character combination that can be used to locate position), don't think can get what you want without VBA. Either loop through string or explore Regular Expressions aka RegEx. Set reference to Microsoft VBScript Regular Expressions x.x library.
Function GetNum(strS AS String)
Dim re As RegExp, Match As Object
Set re = New RegExp
re.Pattern = "[\d+]+"
Set Match = re.Execute(strS)
GetNum = Null
If Match.Count > 0 Then GetNum = Match(0)
End Function
Input of string "Fuel Injector - No 1 - R&I" returns 1.
Place function in a general module and call it from query.
SELECT table.*, GetNum(Nz(txtPMTaskDesc,"")) AS Num FROM table;
Function returns Null if there is no number match.
Well, does the number you want ALWAYS have a - No xxxx - format?
If yes, then you could have this global function in VBA like this:
Public Function GNUM(v As Variant) As Long
If IsNull(v) Then
GNUM = 0
Exit Function
End If
Dim vBuf As Variant
vBuf = Split(v, " - No ")
Dim strRes As String
If UBound(vBuf) > 0 Then
strRes = Split(vBuf(1), "-")(0)
GNUM = Trim(strRes)
Else
GNUM = 0
End If
End Function
Then your sql will be like this:
SELECT BLA, BLA, txtPMTaskDesc, GNUM([txtPMTaskDesc] AS TaskNum
FROM myTable
So you can create/have a public VBA function, and it can be used in the sql query.
It just a question if " - No -" is ALWAYS that format, then THEN the number follows this
So we have "space" "-" "space" "No" "space" "-" -- then the number and the " -"
How well this will work depends on how consistent this text is.
I am writing a program. there is a binary floating number like this format : XX.XXX. for example,binary floating number 01.101 convert to decimal number is 1.625. I tried it for a long time, but couldn't work it out.
I use [4:0]num to store the number. num[4:3] is the integer part, num[2:0] is the floating part. the integer part is easy, when num[2:0]=3'b101, it means that the binary floating part is 0.101, and convert to decimal number is 0.625. so how can I convert the sequence"101", get a sequence "625"?
The quickest way is probably just to use a LUT (lookup table). Since the fractional portion is only 3 bits, that leaves you with only 8 possibilities. And you could use an 12bit value where each nibble is a digit that could be sent to a display etc.
reg result[11:0] // each nibble represents a digit
always #(*) begin
case (num[2:0])
3'b000 : result = 12'h000;
3'b001 : result = 12'h125;
3'b010 : result = 12'h250;
3'b011 : result = 12'h375;
3'b100 : result = 12'h500;
3'b101 : result = 12'h625;
3'b110 : result = 12'h750;
default : result = 12'h875;
endcase
end
How does this denary to binary program work? I am finding it hard to comprehend what is happening behind the code.
Can someone explain the lines 6 onwards?
Number = int(input("Hello. \n\nPlease enter a number to convert: "))
if Number < 0:
print ("Can't be less than 0")
else:
Remainder = 0
String = ""
while Number > 0:
Remainder = Number % 2
Number = Number // 2
String = str(Remainder) + String
print (String)
The idea is to separate out the last part of the binary number, stick it in a buffer, and then remove it from "Number". The method is general and can be used for other bases as well.
Start by looking at it as a dec -> dec "conversion" to understand the principle.
Let's say you have the number 174 (base10). If you want to parse out each individual piece (read as "digit") of it you can calculate the number modulo the base (10), then do an integer division to "remove" that digit from the number. I.e. 174%10 and 174//10 => (Number) 17|4 (Reminder). Next iteration you have 17 from the division and when you perform the same procedure, it'll split it up into 1|7. On the next iteration you'll get 0|1, and after that "Number" will be 0 (which is the exit condition for the loop (while Number > 0)).
In each iteration of the loop you take the remainder (which will be a single digit for the specific base you use (it's a basic property of how bases work)), convert it to a string and concatenate it with the string you had from previous iterations (note the order in the code!), and you'll get the converted number once you've divided your way down to zero.
As mentioned before, this works for any base; you can use base 16 to convert to hex (though you'll need to do some translations for digits above 9), octal (base 8), etc.
Python code for converting denary into binary
denary= int(input('Denary: '))
binary= [0,0,0,0]
while denary>0:
for n,i in enumerate(binary):
if denary//(2**(3-n))>=1:
binary[n]= 1
denary -= 2**(3-n)
print(denary)
print (binary)
I have a query that compares a value. Both values are equal. Data types are double. However, the result is always false. Have you encountered the same scenario?
I tried to round off the data before comparing it and I got the correct results. What do you think is the cause of this issue?
This is a well known side effect of floating numbers.
You have several options. Test for a difference or convert to other data type:
Where Abs(Field1 - Field2) < 0.00001 (or whatever value you consider equal)
Where CCur(Field1) = CCur(Field2)
Where CDec(Field1) = CDecCur(Field2)
Confirm that the values are equal for both variables. Double is floating point with 15 decimal precision, I think. If your display is showing your double variables with rounding, they may appear equal but in fact they may not be equal.
I would recommend you to do Round(variable1, 4) and compare it with Round(variable2, 4). Try testing your variables with a test routine like this:
Public Sub SendDoubles()
Dim a As Double
Dim b As Double
a = 10.0000000001
b = 10.000000001
TestDoubles a, b
End Sub
Public Sub TestDoubles(a As Double, b As Double)
MsgBox CStr(a) & vbCrLf & CStr(b)
MsgBox a = b
MsgBox Round(a, 4) = Round(b, 4)
End Sub
Notice that CStr will convert your double to string so that you can visualize both variable's value. The 2nd MsgBox attempts to compare them as-is and the third one compares them with rounding.
I've tried googling, but with limited luck - what I want to do is use VBA to read in coordinates from a serial GPS dongle (or bluetooth Android phone mimicking the former), log a "visit" record, and find the nearest matching record on a table of our clients. Has anyone seen an opensource script that will allow this?
Thanks in advance.
PG
For the serial port data acquisition see the Serial Port Communications page at my web site. Then you can write records to a table using a SQL Insert query or a DAO Recordset with an AddNew. However finding the nearest matching record will likely require using of geometry to figure out the reading through the table looking for the closest location I'd have to refresh my memory as to the exact equations required.
I'm also thinking that to speed up the search you might want to index the latitude and longitude and start your search at nearby lat/longs. That is limit the initial record set to plus/minus, for example, 0.1 of a lat/long which, just guessing would be about 10 kms square.
Here's a rough rundown of what you will need to do.
Lookup Longitude & Latitude For Your Clients' Addresses:
I posted a question a while back on SO asking how to get GPS Coordinates for an address. You can see that question here. There's actually two functions there for you, one to use Google Maps API and another that uses rpc.geocoder.us. Take your pick. Just be aware that each of them having limitations. Google has licensing restrictions as well as maximum queries per day. Geocoder.us has a limit of one query every 15 seconds and I don't remember what their maximum queries per day is, if they even have a limit.
Obviously, you will need to retrieve the Longitude and Latitude for your addresses beforehand, and store this information along with the address. You could possibly try to come up with a zip code for the area and lookup the addresses by that but it could be horribly inaccurate in sprawling urban areas, especially if you have a lot of customers concentrated in a single zip code. You'll need to take Tony's advice here and query your GPS coordinate data using a between statement to get addresses in the near vicinity.
Get GPS Data from the GPS Device
As Tony has already pointed out, you'll need to use something like an ActiveX control, DLL, or API call to perform serial communications with your GPS Device. I have used MS's communication ActiveX control in the past for getting GPS data and it did work satisfactory.
I don't know where my code is to retrieve the incoming data from the GPS device. It's not exactly trivial if you have never programmed serial communications before. You usually have an OnComm event that fires on incoming data. If I remember correctly you loop until an EOF (End of File) code or bit is found which indicates the end of a data stream. If you use MS's Communications Control you can see some sample code here: http://support.microsoft.com/kb/194922
I think I had to use 4800 in my settings instead of 9600 but your requirements may be different depending what type of device you are using.
Extract The Right Data From Your Incoming GPS Data
Step two is to extract the data you need from the incoming GPS Data. You may even find that you have to change it to match the GPS Data you have stored (see below).
In my sample NMEA sentences (above), the Longitude and Latitude are both in the following format:
ddmm.mmmm
For some programs or API's you may have to convert it into Degrees.Degrees, or to word it another way, dd.dddd. The formula to convert it is:
dd.dddd = ddd + mm.mmmm/60
So, for example, if we want to convert my sample data above to exact Longitude and Latitude in degrees, here's what it would look like:
3731.9404 ----> 37 + 31.9404/60 = 37.53234 degrees
10601.6986 ----> 106 + 1.6986/60 = 106.02831 degrees
Here are some functions I wrote back in 2007 to extract certain parts of the data from the NMEA sentence:
Public Function ExtractLatitude(strNMEAString As String, Optional strNMEAStringType As String = "GPRMC") As String
'This function extracts the latitude from an NMEA string and converts it to Decimal Degrees (as a string).
'To use this function you must specify what string type you are passing in, either GPRMC or GPGGA
Dim aryNMEAString() As String
aryNMEAString() = Split(strNMEAString, ",")
Dim dblMinutes As Single, dblLatitude As Single
Select Case strNMEAStringType
Case "GPRMC"
'Latitude is the Number 3 place in the array (4th place in the string)
If aryNMEAString(2) = "A" Then 'A represents a valid string
dblMinutes = (CDbl(Mid(aryNMEAString(3), 3, 7)) / 60)
dblLatitude = CDbl(Left(aryNMEAString(3), 2)) + dblMinutes
ExtractLatitude = CStr(dblLatitude)
End If
Case "GPGGA"
'Latitude is the Number 2 place in the array (3rd place in the string)
If CDbl(aryNMEAString(2)) <> 0 Then 'If string is invalid it will be 0
dblMinutes = (CDbl(Mid(aryNMEAString(2), 3, 7)) / 60)
dblLatitude = CDbl(Left(aryNMEAString(2), 2)) + dblMinutes
ExtractLatitude = CStr(dblLatitude)
End If
End Select
End Function
Public Function ExtractLongitude(strNMEAString As String, Optional strNMEAStringType As String = "GPRMC") As String
'This function extracts the longitude from an NMEA string and converts it to Decimal Degrees (as a string).
'To use this function you must specify what string type you are passing in, either GPRMC or GPGGA
Dim aryNMEAString() As String
aryNMEAString() = Split(strNMEAString, ",")
Dim dblMinutes As Single, dblLongitude As Single
Select Case strNMEAStringType
Case "GPRMC"
'Latitude is the Number 3 place in the array (4th place in the string)
If aryNMEAString(2) = "A" Then
dblMinutes = (CDbl(Mid(aryNMEAString(5), 4, 7)) / 60)
dblLongitude = CDbl(Left(aryNMEAString(5), 3)) + dblMinutes
ExtractLongitude = CStr(dblLongitude)
End If
Case "GPGGA"
'Latitude is the Number 2 place in the array (3rd place in the string)
If CDbl(aryNMEAString(4)) <> 0 Then
dblMinutes = (CDbl(Mid(aryNMEAString(4), 4, 7)) / 60)
dblLongitude = CDbl(Left(aryNMEAString(4), 3)) + dblMinutes
ExtractLongitude = CStr(dblLongitude)
End If
End Select
End Function
Public Function ExtractSpeed(strGPRMC As String) As Integer
'Expects a GPRMC NMEA Sentence
Dim aryGPRMC() As String, dblSpeed As Double
aryGPRMC() = Split(strGPRMC, ",")
If aryGPRMC(7) <> "" Then dblSpeed = CDbl(aryGPRMC(7))
'Convert knots to MPH
ExtractSpeed = CInt(dblSpeed * 1.15077945)
End Function
Public Function ExtractHeading(strGPRMC As String) As Double
'Expects a GPRMC NMEA Sentence
Dim aryGPRMC() As String
aryGPRMC() = Split(strGPRMC, ",")
If aryGPRMC(8) <> "" Then ExtractHeading = CDbl(aryGPRMC(8))
End Function
Public Function ExtractSatelliteCount(strGPGGA As String) As Integer
'Expects a GPGGA NMEA Sentence
Dim aryGPGGA() As String
aryGPGGA() = Split(strGPGGA, ",")
ExtractSatelliteCount = CInt(aryGPGGA(7))
End Function