{"id":1879,"date":"2015-01-07T00:40:15","date_gmt":"2015-01-07T00:40:15","guid":{"rendered":"http:\/\/41j.com\/blog\/?p=1879"},"modified":"2015-01-17T03:02:22","modified_gmt":"2015-01-17T03:02:22","slug":"safecast-nema-format-notes-bgeigienano","status":"publish","type":"post","link":"https:\/\/41j.com\/blog\/2015\/01\/safecast-nema-format-notes-bgeigienano\/","title":{"rendered":"Safecast NEMA format notes (bGeigieNano)"},"content":{"rendered":"<p><a href=\"www.safecast.org\">Safecast<\/a> use a modified NEMA format on the bGeigieNano for writing logfiles, and when dumping data over the serial port to the XBee interface. I need to parse this for a project, so these are my notes on the format. The line of code that generates the NEMA string follows, and is found <a href=\"https:\/\/github.com\/Safecast\/bGeigieNanoKit\/blob\/master\/bGeigieNano.ino#L879\">here<\/a>:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n  sprintf_P(buf, PSTR(&quot;$%s,%04d,%02d-%02d-%02dT%02d:%02d:%02dZ,%ld,%ld,%ld,%c,%s,%c,%s,%c,%s,%c,%d,%ld&quot;),  \\\r\n              NANO_HEADER, \\\r\n              config.device_id, \\\r\n              year, month, day,  \\\r\n              hour, minute, second, \\\r\n              cpm, \\\r\n              cpb, \\\r\n              total_count, \\\r\n              geiger_status, \\\r\n              lat, NS,\\\r\n              lon, WE,\\\r\n              strbuffer, \\\r\n              gps_status, \\\r\n              nbsat  == TinyGPS::GPS_INVALID_SATELLITES ? 0 : nbsat, \\\r\n              precission == TinyGPS::GPS_INVALID_HDOP ? 0 : precission);\r\n<\/pre>\n<p>Here are a few example strings I captured (though in these strings the reading is marked as VOID):<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n$BNRDD,0000,2015-01-06T13:49:32Z,0,0,0,V,0000.0000,N,00000.0000,E,0.00,V,0,0*4A\r\n$BNRDD,0000,2015-01-06T13:49:55Z,0,0,0,V,3537.2793,N,13938.0878,E,110.60,A,3,558B\r\n$BNRDD,0000,2015-01-06T13:50:23Z,0,0,0,V,3537.2776,N,13938.0777,E,105.20,A,4,3036\r\n$BNRDD,0000,2015-01-06T13:52:58Z,15,15,15,V,3537.2776,N,13938.0735,E,100.80,A,4,3\r\n$BNRDD,0000,2015-01-06T13:53:05Z,28,13,28,V,3537.2778,N,13938.0740,E,100.70,A,4,F\r\n$BNRDD,0000,2015-01-06T13:53:10Z,33,5,33,V,3537.2779,N,13938.0744,E,100.70,A,4,39\r\n$BNRDD,0000,2015-01-06T13:53:16Z,89,56,89,V,3537.2776,N,13938.0740,E,100.60,A,4,0\r\n$BNRDD,0000,2015-01-06T13:53:21Z,128,39,128,V,3537.2771,N,13938.0732,E,100.60,A,F\r\n$BNRDD,0000,2015-01-06T13:53:27Z,128,0,128,V,3537.2777,N,13938.0743,E,100.50,A,40\r\n$BNRDD,0000,2015-01-06T13:53:32Z,128,0,128,V,3537.2775,N,13938.0751,E,100.30,A\r\n<\/pre>\n<p>The format can be summarized as follows (values in brackets indicate variables):<\/p>\n<pre>\r\n$BNRDD,[4digit_device_id],[ISO8601_time],[cpm],[cpb],[total_count],[geiger_status],[lat],[NS],[long],[WE],[altitude],[gps_status],[nbsat],[precision]\r\n<\/pre>\n<p>4digit_device_id: This defaults to 0000, but can be set from &#8220;did&#8221; in SAFECAST.TXT on the SD card.<br \/>\ncpm: Count per minute. I don&#8217;t believe this is the count over the last min, but rather an average using a variable window size dependent on the current rate.<br \/>\ncpb: As I understand &#8220;count per bin&#8221;. I think count in the last time period (last second by default?)<br \/>\ntotal_count: Total event count since the device was turned on.<br \/>\ngeiger_status: The device needs to acquire at least 12 windows of data until it is considered valid. &#8220;V&#8221; for VOID or &#8220;A&#8221; for available.<br \/>\nlat: Latitude float, 4.4<br \/>\nNS: N or S, North or South<br \/>\nlong: Longitude 4.4<br \/>\nWE: W or E, West or East<br \/>\naltitude: altitude<br \/>\ngps_status: &#8220;V&#8221; for VOID or &#8220;A&#8221; for available.<br \/>\nnbsat: Satellite count<br \/>\nprecision: GPS precision<\/p>\n<p>There&#8217;s also a Safecast JSON format. This is used by the Safecast webservice API to post data. It looks like this:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n{&quot;longitude&quot;:&quot;111.1111&quot;,&quot;latitude&quot;:&quot;11.1111&quot;,&quot;device_id&quot;:&quot;47&quot;,&quot;value&quot;:&quot;63&quot;,&quot;unit&quot;:&quot;cpm&quot;}\r\n<\/pre>\n<p>The following C function converts from NEMA to JSON format, it also adds a &#8220;captured_at&#8221; JSON field with the time shown in the NEMA string. captured_at is used by the safecast API when measurements are download, but I don&#8217;t think it&#8217;s normal present when a measurement is posted. The function also returns true if the measurement is valid and false if it&#8217;s not. The code has only been briefly tested, please comment with fixes:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;stdio.h&gt;\r\n#include &lt;string.h&gt;\r\n\r\n\/\/ json_string needs to be preallocated and large enough to hold the output\r\nint safecast_nema2json(const char *nema_string,char *json_string) {\r\n\r\n  int    device_id;\r\n  char   iso_timestr&#x5B;50];\r\n  int    cpm;\r\n  int    cpb;\r\n  int    total_count;\r\n  char   geiger_status;\r\n  float  latitude;\r\n  char   NorS;\r\n  float  longitude;\r\n  char   WorE;\r\n  float  altitude;\r\n  char   gps_status;\r\n  int    nbsat;\r\n  char    precision&#x5B;50];\r\n\r\n  iso_timestr&#x5B;0]=0;\r\n\r\n  nsscanf(nema_string,\r\n         &quot;$BNRDD,%04d,%&#x5B;^,],%d,%d,%d,%c,%f,%c,%f,%c,%f,%c,%d,%s&quot;,\r\n         &amp;device_id,\r\n         iso_timestr,\r\n         &amp;cpm,\r\n         &amp;cpb,\r\n         &amp;total_count,\r\n         &amp;geiger_status,\r\n         &amp;latitude,\r\n         &amp;NorS,\r\n         &amp;longitude,\r\n         &amp;WorE,\r\n         &amp;altitude,\r\n         &amp;gps_status,\r\n         &amp;nbsat,\r\n         precision);\r\n\r\n  if(NorS == 'S') latitude  = 0-latitude;\r\n  if(WorE == 'W') longitude = 0-longitude;\r\n\r\n  sprintf(json_string,&quot;{\\&quot;captured_at\\&quot;:\\&quot;%s\\&quot;,\\&quot;device_id\\&quot;:\\&quot;%d\\&quot;,\\&quot;value\\&quot;:\\&quot;%d\\&quot;,\\&quot;unit\\&quot;:\\&quot;cpm\\&quot;, \\&quot;longitude\\&quot;:\\&quot;%f\\&quot;, \\&quot;latitude\\&quot;:\\&quot;%f\\&quot;  }\\n&quot;, iso_timestr, device_id,cpm, longitude,latitude);\r\n\r\n  if((gps_status == 'A') &amp;&amp; (geiger_status == 'A')) return 1;\r\n                                               else return 0;\r\n}\r\n\r\nint main() {\r\n\r\n  char json&#x5B;1024];\r\n\r\n  int safecast_nema_valid = safecast_nema2json(&quot;$BNRDD,0101,2015-01-06T15:11:28Z,11,12,128,V,3537.2618,N,13938.0256,E,40.70,A,5,138*64&quot;,json);\r\n\r\n  if( safecast_nema_valid) printf(&quot;VALIDJSON: %s\\n&quot;,json);\r\n  if(!safecast_nema_valid) printf(&quot;INVALIDJSON: %s\\n&quot;,json);\r\n}\r\n\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Safecast use a modified NEMA format on the bGeigieNano for writing logfiles, and when dumping data over the serial port to the XBee interface. I need to parse this for a project, so these are my notes on the format. The line of code that generates the NEMA string follows, and is found here: sprintf_P(buf, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1],"tags":[],"class_list":["post-1879","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1RRoU-uj","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1879","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/comments?post=1879"}],"version-history":[{"count":5,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1879\/revisions"}],"predecessor-version":[{"id":1894,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/posts\/1879\/revisions\/1894"}],"wp:attachment":[{"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/media?parent=1879"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/categories?post=1879"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/41j.com\/blog\/wp-json\/wp\/v2\/tags?post=1879"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}