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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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):
1 2 3 4 5 6 7 8 9 10 | $BNRDD,0000,2015-01-06T13:49:32Z,0,0,0,V,0000.0000,N,00000.0000,E,0.00,V,0,0*4A $BNRDD,0000,2015-01-06T13:49:55Z,0,0,0,V,3537.2793,N,13938.0878,E,110.60,A,3,558B $BNRDD,0000,2015-01-06T13:50:23Z,0,0,0,V,3537.2776,N,13938.0777,E,105.20,A,4,3036 $BNRDD,0000,2015-01-06T13:52:58Z,15,15,15,V,3537.2776,N,13938.0735,E,100.80,A,4,3 $BNRDD,0000,2015-01-06T13:53:05Z,28,13,28,V,3537.2778,N,13938.0740,E,100.70,A,4,F $BNRDD,0000,2015-01-06T13:53:10Z,33,5,33,V,3537.2779,N,13938.0744,E,100.70,A,4,39 $BNRDD,0000,2015-01-06T13:53:16Z,89,56,89,V,3537.2776,N,13938.0740,E,100.60,A,4,0 $BNRDD,0000,2015-01-06T13:53:21Z,128,39,128,V,3537.2771,N,13938.0732,E,100.60,A,F $BNRDD,0000,2015-01-06T13:53:27Z,128,0,128,V,3537.2777,N,13938.0743,E,100.50,A,40 $BNRDD,0000,2015-01-06T13:53:32Z,128,0,128,V,3537.2775,N,13938.0751,E,100.30,A |
The format can be summarized as follows (values in brackets indicate variables):
$BNRDD,[4digit_device_id],[ISO8601_time],[cpm],[cpb],[total_count],[geiger_status],[lat],[NS],[long],[WE],[altitude],[gps_status],[nbsat],[precision]
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:
1 | {"longitude":"111.1111","latitude":"11.1111","device_id":"47","value":"63","unit":"cpm"} |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #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]; iso_timestr[0]=0; nsscanf(nema_string, "$BNRDD,%04d,%[^,],%d,%d,%d,%c,%f,%c,%f,%c,%f,%c,%d,%s" , &device_id, iso_timestr, &cpm, &cpb, &total_count, &geiger_status, &latitude, &NorS, &longitude, &WorE, &altitude, &gps_status, &nbsat, precision); 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); } |