I am quite interested in using .NET features to access hardware via Mathematica. I have sorted out how access NI USB-6009 (via NetAssembly), DrDAQ (via 32-bit DLL) and Arduino (via .NET SerialIO) boards but I am still not comfortable with the way I am using .NET calls.
UDP NTP Access
As an example, here is a self-contained piece of code that accesses NTP servers using the .NET UDP class. Most of the code is concerned with interpreting the 48-Byte NTP package and it is essentially my test code. The code is "clean" in the sense that if you run the last lines following the Grid command everything closes down perfectly, which is important as long as you play with the code.
Install .NET:
Needs["NETLink`"];
InstallNET[];
Prepare the NTP UDP request package (which consists of 48 bytes):
ntpServer ="192.53.103.108"; (* PTB server Braunschweig Germany *)
(* or *)
ntpServer = "64.90.182.55"; (* NIST server, NY, USA *)
packetLength = 48;
packet = Table[0, {packetLength}];
packet[[1 ;; 4]] = {2^^11100011, 10^^0, 10^^6, 16^^EC};
packet[[13 ;; 16]] = {10^^49, 16^^4E, 10^^49, 10^^52};
sendPacket = NETNew["System.Byte[]", packetLength];
sendPacket = packet;
receivePacket = NETNew["System.Byte[]", packetLength];
udp = NETNew["System.Net.Sockets.UdpClient"];
endPoint = NETNew["System.Net.IPEndPoint", 1, 1];
udp@Connect[ntpServer, 123];
Define interpretations of values returned via the NTP protocol:
leapIndicatorList = {"No warning", "Last minute of day has 61 sec",
"Last minute of day has 59 sec", "Alarm"};
modeList = {"Unknown", "Symmetric Active", "Symmetric Passive", "Client",
"Server", "Broadcast" , "NTP control message"};
stratumList = {"Unspecified", "Primary Server", "Secondary Server",
"Unsynchronized", "Reserved"};
Testing and gathering some information:
In[22]:= err = udp@Send[sendPacket, packetLength]
Out[22]= 48
In[23]:= available = udp@Available[]
Out[23]= 0
In[24]:= broadcatsQ = udp@EnableBroadcast[];
In[25]:= ttlNumber = udp@Ttl[];
In[26]:= receivePacket = udp@Receive[endPoint];
In[27]:= endPoint@Address@ToString[]
Out[27]= "64.90.182.55"
In[28]:= endPoint@ToString[]
Out[28]= "64.90.182.55:123"
In[29]:= NETObjectToExpression[endPoint@ToString[]]
Out[29]= "64.90.182.55:123"
In[30]:= dataPacket = NETObjectToExpression[receivePacket]
Out[30]= {36, 1, 6, 227, 0, 0, 0, 0, 0, 0, 0, 0, 65, 67, 84, 83, 210, 237, 24, 58, 14, \
17, 190, 191, 0, 0, 0, 0, 0, 0, 0, 0, 210, 237, 24, 65, 38, 226, 85, 160, \
210, 237, 24, 65, 38, 227, 17, 28}
In[31]:= leapIndicator = BitShiftRight[BitAnd[dataPacket[[1]], 2^^11000000], 6]
Out[31]= 0
In[32]:= versionNumber = BitShiftRight[BitAnd[dataPacket[[1]], 2^^00111000], 3]
Out[32]= 4
In[33]:= mode = BitAnd[dataPacket[[1]], 2^^00000111]
Out[33]= 4
In[34]:= stratum = dataPacket[[2]]
Out[34]= 1
In[35]:= stratumText = Which[
stratum == 0, stratumList[[1]],
stratum == 1, stratumList[[2]],
2 <= stratum <= 15, stratumList[[3]],
stratum == 16, stratumList[[4]],
stratum > 16, stratumList[[5]]
]
Out[35]= "Primary Server"
In[36]:= poll = If[dataPacket[[3]] > 127, -(255 - dataPacket[[3]]), dataPacket[[3]]]
Out[36]= 6
In[37]:= pollValue = 2^poll
Out[37]= 64
In[38]:= precision =
If[dataPacket[[4]] > 127, -(255 - dataPacket[[4]]), dataPacket[[4]]]
Out[38]= -28
In[39]:= precisionValue = 1000.0 2^precision
Out[39]= 3.72529*10^-6
In[40]:= rootDelay = dataPacket[[5 ;; 8]]
Out[40]= {0, 0, 0, 0}
In[41]:= rootDelayValue =
1000.0 (Fold[256 #1 + #2 &, 0, dataPacket[[5 ;; 6]]] +
Fold[256 #1 + #2 &, 0, dataPacket[[7 ;; 8]]]/2^16)
Out[41]= 0.
In[42]:= rootDispersion = dataPacket[[9 ;; 12]]
Out[42]= {0, 0, 0, 0}
In[43]:= rootDispersionValue =
1000.0 (Fold[256 #1 + #2 &, 0, dataPacket[[9 ;; 10]]] +
Fold[256 #1 + #2 &, 0, dataPacket[[11 ;; 12]]]/2^16)
Out[43]= 0.
In[44]:= refIdentifier = dataPacket[[13 ;; 16]]
Out[44]= {65, 67, 84, 83}
In[45]:= refIdentifierString =
If[Fold[And, True, (64 < # < 123 || # == 0) & /@ refIdentifier],
FromCharacterCode[refIdentifier],
ToString[dataPacket[[13]]] <> "." <> ToString[dataPacket[[14]]] <> "." <>
ToString[dataPacket[[15]]] <> "." <> ToString[dataPacket[[16]]]]
Out[45]= "ACTS"
In[46]:= refTimestamp = Fold[256 #1 + #2 &, 0, dataPacket[[17 ;; 20]]]
Out[46]= 3538753594
In[47]:= refTimestampMS = 1000.0 Fold[256 #1 + #2 &, 0, dataPacket[[21 ;; 24]]]/2^32
Out[47]= 54.9583
In[48]:= orgTimestamp = Fold[256 #1 + #2 &, 0, dataPacket[[25 ;; 28]]]
Out[48]= 0
In[49]:= orgTimestampMS = 1000.0 Fold[256 #1 + #2 &, 0, dataPacket[[29 ;; 32]]]/2^32
Out[49]= 0.
In[50]:= rcvTimestamp = Fold[256 #1 + #2 &, 0, dataPacket[[33 ;; 36]]]
Out[50]= 3538753601
In[52]:= rcvTimestampMS = 1000.0 Fold[256 #1 + #2 &, 0, dataPacket[[37 ;; 40]]]/2^32
Out[52]= 151.891
In[53]:= trmTimestamp = Fold[256 #1 + #2 &, 0, dataPacket[[41 ;; 44]]]
Out[53]= 3538753601
In[54]:= trmTimestampMS = 1000.0 Fold[256 #1 + #2 &, 0, dataPacket[[45 ;; 48]]]/2^32
Out[54]= 151.902
In[55]:= DateString[trmTimestamp, TimeZone -> 2]
Out[55]= "Mon 20 Feb 2012 20:06:41"
In[56]:= endPoint@ToString[]
Out[56]= "64.90.182.55:123"
Now we display all of the information obtained above in a Grid:
myPadding[x_] :=
If[x >= 100, PaddedForm[x, {5, 2}, NumberPadding -> {"", "0"}],
PaddedForm[x, {4, 2}, NumberPadding -> {"0", "0"}]];
Grid[{{"Descriptions", "NTP returned value",
"NTP interpreted value"}, {"End point", ntpServer,
endPoint@ToString[]}, {"Leap Indicator", leapIndicator,
leapIndicatorList[[leapIndicator + 1]]}, {"Version Number", versionNumber,
versionNumber}, {"Mode", mode, modeList[[mode + 1]]}, {"Stratum", stratum,
stratumText}, {"Poll Interval", poll, pollValue}, {"Precision", precision,
precisionValue " ms"}, {"Root Delay", rootDelay,
rootDelayValue " ms"}, {"Root Dispersion", rootDispersion,
rootDispersionValue " ms"}, {"Reference Identifier", refIdentifier,
refIdentifierString}, {"Reference Timestamp", refTimestamp,
DateString[refTimestamp] <> " (+ " <>
ToString[refTimestampMS // myPadding] <> " ms)"}, {"Originate Timestamp",
orgTimestamp,
DateString[orgTimestamp] <> " (+ " <>
ToString[orgTimestampMS // myPadding] <> " ms)"}, {"Receive Timestamp",
rcvTimestamp,
DateString[rcvTimestamp] <> " (+ " <>
ToString[rcvTimestampMS // myPadding] <> " ms)"}, {"Transmit Timestamp",
trmTimestamp,
DateString[trmTimestamp] <> " (+ " <>
ToString[trmTimestampMS // myPadding] <> " ms)"}}, Frame -> All,
ItemStyle -> Directive[FontFamily -> "Courier", FontSize -> 14],
Alignment -> {{Right, Right, Right}, Top},
Background -> {None, {Lighter[Yellow, .9], {White,
Lighter[Blend[{Blue, Green}], .8]}}},
Dividers -> {{}, Table[(2 + 12 n) -> Thickness[2], {n, 0, 4}]},
Spacings -> {5, 1}]
Cleanup:
udp@Close[];
ReleaseNETObject[udp];
ReleaseNETObject[sendPacket];
ReleaseNETObject[receivePacket];
ReleaseNETObject[endPoint];
UninstallNET[];
Out[n]lines that cannot be pasted properly... Please don't dump code on us and expect us to go through it. Reduce your problem to a minimal working example and explain clearly what the problem is and what you're trying to do. Voting to close as NaRQ until it is fixed. – R.M Feb 20 at 19:36