42
42
43
43
"""
44
44
45
+
45
46
import time
46
47
import struct
47
48
@@ -60,7 +61,6 @@ def __init__(self, uart, baudrate=19200):
60
61
61
62
def _uart_xfer (self , cmd ):
62
63
"""Send AT command and return response as tuple of lines read."""
63
-
64
64
self ._uart .reset_input_buffer ()
65
65
self ._uart .write (str .encode ("AT" + cmd + "\r " ))
66
66
@@ -80,9 +80,13 @@ def reset(self):
80
80
self ._uart_xfer ("&F0" ) # factory defaults
81
81
self ._uart_xfer ("&K0" ) # flow control off
82
82
83
+ def _transfer_buffer (self ):
84
+ """Copy out buffer to in buffer to simulate receiving a message."""
85
+ self ._uart_xfer ("+SBDTC" )
86
+
83
87
@property
84
88
def data_out (self ):
85
- "The binary data in the outbound buffer."
89
+ """ The binary data in the outbound buffer."" "
86
90
return self ._buf_out
87
91
88
92
@data_out .setter
@@ -199,6 +203,220 @@ def model(self):
199
203
return resp [1 ].strip ().decode ()
200
204
return None
201
205
202
- def _transfer_buffer (self ):
203
- """Copy out buffer to in buffer to simulate receiving a message."""
204
- self ._uart_xfer ("+SBDTC" )
206
+ @property
207
+ def serial_number (self ):
208
+ """Modem's serial number, also known as the modem's IMEI.
209
+
210
+ Returns
211
+ string
212
+ """
213
+ resp = self ._uart_xfer ("+CGSN" )
214
+ if resp [- 1 ].strip ().decode () == "OK" :
215
+ return resp [1 ].strip ().decode ()
216
+ return None
217
+
218
+ @property
219
+ def signal_quality (self ):
220
+ """Signal Quality also known as the Received Signal Strength Indicator (RSSI).
221
+
222
+ Values returned are 0 to 5, where 0 is no signal (0 bars) and 5 is strong signal (5 bars).
223
+
224
+ Important note: signal strength may not be fully accurate, so waiting for
225
+ high signal strength prior to sending a message isn't always recommended.
226
+ For details see https://docs.rockblock.rock7.com/docs/checking-the-signal-strength
227
+
228
+ Returns:
229
+ int
230
+ """
231
+ resp = self ._uart_xfer ("+CSQ" )
232
+ if resp [- 1 ].strip ().decode () == "OK" :
233
+ return int (resp [1 ].strip ().decode ().split (":" )[1 ])
234
+ return None
235
+
236
+ @property
237
+ def revision (self ):
238
+ """Modem's internal component firmware revisions.
239
+
240
+ For example: Call Processor Version, Modem DSP Version, DBB Version (ASIC),
241
+ RFA VersionSRFA2), NVM Version, Hardware Version, BOOT Version
242
+
243
+ Returns a tuple:
244
+ (string, string, string, string, string, string, string)
245
+ """
246
+ resp = self ._uart_xfer ("+CGMR" )
247
+ if resp [- 1 ].strip ().decode () == "OK" :
248
+ lines = []
249
+ for x in range (1 , len (resp ) - 2 ):
250
+ line = resp [x ]
251
+ if line != b"\r \n " :
252
+ lines .append (line .decode ().strip ())
253
+ return tuple (lines )
254
+ return (None ,) * 7
255
+
256
+ @property
257
+ def ring_alert (self ):
258
+ """The current ring indication mode.
259
+
260
+ False means Ring Alerts are disabled, and True means Ring Alerts are enabled.
261
+
262
+ When SBD ring indication is enabled, the ISU asserts the RI line and issues
263
+ the unsolicited result code SBDRING when an SBD ring alert is received.
264
+ (Note: the network can only send ring alerts to the ISU after it has registered).
265
+
266
+ Returns:
267
+ bool
268
+ """
269
+ resp = self ._uart_xfer ("+SBDMTA?" )
270
+ if resp [- 1 ].strip ().decode () == "OK" :
271
+ return bool (int (resp [1 ].strip ().decode ().split (":" )[1 ]))
272
+ return None
273
+
274
+ @ring_alert .setter
275
+ def ring_alert (self , value ):
276
+ if value in (True , False ):
277
+ resp = self ._uart_xfer ("+SBDMTA=" + str (int (value )))
278
+ if resp [- 1 ].strip ().decode () == "OK" :
279
+ return True
280
+ raise RuntimeError ("Error setting Ring Alert." )
281
+ raise ValueError (
282
+ "Use 0 or False to disable Ring Alert or use 1 or True to enable Ring Alert."
283
+ )
284
+
285
+ @property
286
+ def ring_indication (self ):
287
+ """The ring indication status.
288
+
289
+ Returns the reason for the most recent assertion of the Ring Indicate signal.
290
+
291
+ The response contains separate indications for telephony and SBD ring indications.
292
+ The response is in the form:
293
+ (<tel_ri>,<sbd_ri>)
294
+
295
+ <tel_ri> indicates the telephony ring indication status:
296
+ 0 No telephony ring alert received.
297
+ 1 Incoming voice call.
298
+ 2 Incoming data call.
299
+ 3 Incoming fax call.
300
+
301
+ <sbd_ri> indicates the SBD ring indication status:
302
+ 0 No SBD ring alert received.
303
+ 1 SBD ring alert received.
304
+
305
+ Returns a tuple:
306
+ (string, string)
307
+ """
308
+ resp = self ._uart_xfer ("+CRIS" )
309
+ if resp [- 1 ].strip ().decode () == "OK" :
310
+ return tuple (resp [1 ].strip ().decode ().split (":" )[1 ].split ("," ))
311
+ return (None ,) * 2
312
+
313
+ @property
314
+ def geolocation (self ):
315
+ """Most recent geolocation of the modem as measured by the Iridium constellation
316
+ including a timestamp of when geolocation measurement was made.
317
+
318
+ The response is in the form:
319
+ (<x>, <y>, <z>, <timestamp>)
320
+
321
+ <x>, <y>, <z> is a geolocation grid code from an earth centered Cartesian coordinate system,
322
+ using dimensions, x, y, and z, to specify location. The coordinate system is aligned
323
+ such that the z-axis is aligned with the north and south poles, leaving the x-axis
324
+ and y-axis to lie in the plane containing the equator. The axes are aligned such that
325
+ at 0 degrees latitude and 0 degrees longitude, both y and z are zero and
326
+ x is positive (x = +6376, representing the nominal earth radius in kilometres).
327
+ Each dimension of the geolocation grid code is displayed in decimal form using
328
+ units of kilometres. Each dimension of the geolocation grid code has a minimum value
329
+ of –6376, a maximum value of +6376, and a resolution of 4.
330
+ This geolocation coordinate system is known as ECEF (acronym earth-centered, earth-fixed),
331
+ also known as ECR (initialism for earth-centered rotational)
332
+
333
+ <timestamp> is a time_struct
334
+ The timestamp is assigned by the modem when the geolocation grid code received from
335
+ the network is stored to the modem's internal memory.
336
+
337
+ The timestamp used by the modem is Iridium system time, which is a running count of
338
+ 90 millisecond intervals, since Sunday May 11, 2014, at 14:23:55 UTC (the most recent
339
+ Iridium epoch).
340
+ The timestamp returned by the modem is a 32-bit integer displayed in hexadecimal form.
341
+ We convert the modem's timestamp and return it as a time_struct.
342
+
343
+ The system time value is always expressed in UTC time.
344
+
345
+ Returns a tuple:
346
+ (int, int, int, time_struct)
347
+ """
348
+ resp = self ._uart_xfer ("-MSGEO" )
349
+ if resp [- 1 ].strip ().decode () == "OK" :
350
+ temp = resp [1 ].strip ().decode ().split (":" )[1 ].split ("," )
351
+ ticks_since_epoch = int (temp [3 ], 16 )
352
+ ms_since_epoch = (
353
+ ticks_since_epoch * 90
354
+ ) # convert iridium ticks to milliseconds
355
+
356
+ # milliseconds to seconds
357
+ # hack to divide by 1000 and avoid using limited floating point math which throws the
358
+ # calculations off quite a bit, this should be accurate to 1 second or so
359
+ ms_str = str (ms_since_epoch )
360
+ substring = ms_str [0 : len (ms_str ) - 3 ]
361
+ secs_since_epoch = int (substring )
362
+
363
+ # iridium epoch
364
+ iridium_epoch = time .struct_time (((2014 ), (5 ), 11 , 14 , 23 , 55 , 6 , - 1 , - 1 ))
365
+ iridium_epoch_unix = time .mktime (iridium_epoch )
366
+
367
+ # add timestamp's seconds to the iridium epoch
368
+ time_now_unix = iridium_epoch_unix + int (secs_since_epoch )
369
+ return (
370
+ int (temp [0 ]),
371
+ int (temp [1 ]),
372
+ int (temp [2 ]),
373
+ time .localtime (time_now_unix ),
374
+ )
375
+ return (None ,) * 4
376
+
377
+ @property
378
+ def system_time (self ):
379
+ """Current date and time as given by the Iridium network.
380
+
381
+ The system time is available and valid only after the ISU has registered with
382
+ the network and has received the Iridium system time from the network.
383
+ Once the time is received, the ISU uses its internal clock to increment the counter.
384
+ In addition, at least every 8 hours, or on location update or other event that
385
+ requires re-registration, the ISU will obtain a new system time from the network.
386
+
387
+ The timestamp used by the modem is Iridium system time, which is a running count of
388
+ 90 millisecond intervals, since Sunday May 11, 2014, at 14:23:55 UTC (the most recent
389
+ Iridium epoch).
390
+ The timestamp returned by the modem is a 32-bit integer displayed in hexadecimal form.
391
+ We convert the modem's timestamp and return it as a time_struct.
392
+
393
+ The system time value is always expressed in UTC time.
394
+
395
+ Returns:
396
+ time_struct
397
+ """
398
+ resp = self ._uart_xfer ("-MSSTM" )
399
+ if resp [- 1 ].strip ().decode () == "OK" :
400
+ temp = resp [1 ].strip ().decode ().split (":" )[1 ]
401
+ if temp == " no network service" :
402
+ return None
403
+ ticks_since_epoch = int (temp , 16 )
404
+ ms_since_epoch = (
405
+ ticks_since_epoch * 90
406
+ ) # convert iridium ticks to milliseconds
407
+
408
+ # milliseconds to seconds\
409
+ # hack to divide by 1000 and avoid using limited floating point math which throws the
410
+ # calculations off quite a bit, this should be accurate to 1 second or so
411
+ ms_str = str (ms_since_epoch )
412
+ substring = ms_str [0 : len (ms_str ) - 3 ]
413
+ secs_since_epoch = int (substring )
414
+
415
+ # iridium epoch
416
+ iridium_epoch = time .struct_time (((2014 ), (5 ), 11 , 14 , 23 , 55 , 6 , - 1 , - 1 ))
417
+ iridium_epoch_unix = time .mktime (iridium_epoch )
418
+
419
+ # add timestamp's seconds to the iridium epoch
420
+ time_now_unix = iridium_epoch_unix + int (secs_since_epoch )
421
+ return time .localtime (time_now_unix )
422
+ return None
0 commit comments