Safecast NEMA format notes (bGeigieNano)

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, PSTR("$%s,%04d,%02d-%02d-%02dT%02d:%02d:%02dZ,%ld,%ld,%ld,%c,%s,%c,%s,%c,%s,%c,%d,%ld"),  \
              NANO_HEADER, \
              config.device_id, \
              year, month, day,  \
              hour, minute, second, \
              cpm, \
              cpb, \
              total_count, \
              geiger_status, \
              lat, NS,\
              lon, WE,\
              strbuffer, \
              gps_status, \
              nbsat  == TinyGPS::GPS_INVALID_SATELLITES ? 0 : nbsat, \
              precission == TinyGPS::GPS_INVALID_HDOP ? 0 : precission);

Here are a few example strings I captured (though in these strings the reading is marked as VOID):


The format can be summarized as follows (values in brackets indicate variables):


4digit_device_id: This defaults to 0000, but can be set from “did” in SAFECAST.TXT on the SD card.
cpm: Count per minute. I don’t believe this is the count over the last min, but rather an average using a variable window size dependent on the current rate.
cpb: As I understand “count per bin”. I think count in the last time period (last second by default?)
total_count: Total event count since the device was turned on.
geiger_status: The device needs to acquire at least 12 windows of data until it is considered valid. “V” for VOID or “A” for available.
lat: Latitude float, 4.4
NS: N or S, North or South
long: Longitude 4.4
WE: W or E, West or East
altitude: altitude
gps_status: “V” for VOID or “A” for available.
nbsat: Satellite count
precision: GPS precision

There’s also a Safecast JSON format. This is used by the Safecast webservice API to post data. It looks like this:


The following C function converts from NEMA to JSON format, it also adds a “captured_at” 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’t think it’s normal present when a measurement is posted. The function also returns true if the measurement is valid and false if it’s not. The code has only been briefly tested, please comment with fixes:

#include <stdio.h>
#include <string.h>

// json_string needs to be preallocated and large enough to hold the output
int safecast_nema2json(const char *nema_string,char *json_string) {

  int    device_id;
  char   iso_timestr[50];
  int    cpm;
  int    cpb;
  int    total_count;
  char   geiger_status;
  float  latitude;
  char   NorS;
  float  longitude;
  char   WorE;
  float  altitude;
  char   gps_status;
  int    nbsat;
  char    precision[50];



  if(NorS == 'S') latitude  = 0-latitude;
  if(WorE == 'W') longitude = 0-longitude;

  sprintf(json_string,"{\"captured_at\":\"%s\",\"device_id\":\"%d\",\"value\":\"%d\",\"unit\":\"cpm\", \"longitude\":\"%f\", \"latitude\":\"%f\"  }\n", iso_timestr, device_id,cpm, longitude,latitude);

  if((gps_status == 'A') && (geiger_status == 'A')) return 1;
                                               else return 0;

int main() {

  char json[1024];

  int safecast_nema_valid = safecast_nema2json("$BNRDD,0101,2015-01-06T15:11:28Z,11,12,128,V,3537.2618,N,13938.0256,E,40.70,A,5,138*64",json);

  if( safecast_nema_valid) printf("VALIDJSON: %s\n",json);
  if(!safecast_nema_valid) printf("INVALIDJSON: %s\n",json);