A database.lisp => database.lisp +229 -0
@@ 0,0 1,229 @@
+;; database.lisp
+
+;; Copyright (c) 2023 Jeremiah LaRocco <jeremiah_larocco@fastmail.com>
+
+;; Permission to use, copy, modify, and/or distribute this software for any
+;; purpose with or without fee is hereby granted, provided that the above
+;; copyright notice and this permission notice appear in all copies.
+
+;; THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+;; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+;; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+;; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+;; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+;; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+;; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+(in-package :racebox-tools)
+
+(defun create-db (db)
+ (loop :for sql-file :in (uiop:directory-files (asdf:system-relative-pathname :racebox-tools "sql/")
+ "create_table*.sql")
+ :for the-sql = (alexandria:read-file-into-string sql-file)
+ :do
+ (sqlite:execute-non-query db the-sql)))
+
+(defgeneric insert-message (db msg)
+ (:documentation "Add msg to db."))
+
+(defmethod insert-message (db (msg racebox-mini-data-message))
+ (with-slots (itow
+ year
+ month
+ day
+
+ hour
+ minute
+ second
+
+ validity-flags
+ time-accuraccy
+ nanosecond
+
+ fix-status
+ fix-status-flags
+ date-time-flags
+ number-of-svs
+
+ longitude
+ latitude
+
+ wgs-altitude
+ msl-altitude
+
+ horizontal-accuracy
+ vertical-accuracy
+
+ speed
+ heading
+
+ speed-accuracy
+ heading-accuracy
+
+ pdop
+
+ lat-lon-flags
+
+ battery-status
+
+ g-force-x
+ g-force-y
+ g-force-z
+
+ rotation-rate-x
+ rotation-rate-y
+ rotation-rate-z
+ ) msg
+
+ (sqlite:execute-non-query
+ db
+ "INSERT INTO raw_data (
+ itow,
+
+ year,
+ month,
+ day,
+
+ hour,
+ minute,
+ second,
+
+ validity_flags,
+ time_accuraccy,
+ nanosecond,
+
+ fix_status,
+ fix_status_flags,
+ date_time_flags,
+ number_of_svs,
+
+ longitude,
+ latitude,
+
+ wgs_altitude,
+ msl_altitude,
+
+ horizontal_accuracy,
+ vertical_accuracy,
+
+ speed,
+ heading,
+
+ speed_accuracy,
+ heading_accuracy,
+
+ pdop,
+
+ lat_lon_flags,
+
+ battery_status,
+
+ g_force_x,
+ g_force_y,
+ g_force_z,
+
+ rotation_rate_x,
+ rotation_rate_y,
+ rotation_rate_z
+) values
+ (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
+ itow
+
+ year
+ month
+ day
+
+ hour
+ minute
+ second
+
+ validity-flags
+ time-accuraccy
+ nanosecond
+
+ fix-status
+ fix-status-flags
+ date-time-flags
+ number-of-svs
+
+ longitude
+ latitude
+
+ wgs-altitude
+ msl-altitude
+
+ horizontal-accuracy
+ vertical-accuracy
+
+ speed
+ heading
+
+ speed-accuracy
+ heading-accuracy
+
+ pdop
+
+ lat-lon-flags
+
+ battery-status
+
+ g-force-x
+ g-force-y
+ g-force-z
+
+ rotation-rate-x
+ rotation-rate-y
+ rotation-rate-z))
+ (sqlite:last-insert-rowid db))
+
+(defmethod insert-message (db (msg gps-message))
+ (with-slots (timestamp
+ longitude
+ latitude
+ msl-altitude
+ wgs-altitude
+ speed
+ heading
+ g-force
+ rotation
+ raw-id) msg
+ (sqlite:execute-non-query
+ db
+ "INSERT INTO gps_message (
+ timestamp,
+ longitude,
+ latitude,
+ msl_altitude,
+ wgs_altitude,
+ speed,
+ heading,
+ g_force_x,
+ g_force_y,
+ g_force_z,
+ rotation_x,
+ rotation_y,
+ rotation_z,
+ raw_id
+) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)
+"
+ (local-time:format-timestring nil timestamp)
+ longitude
+ latitude
+ msl-altitude
+ wgs-altitude
+ speed
+ heading
+ (vx g-force)
+ (vy g-force)
+ (vz g-force)
+ (vx rotation)
+ (vy rotation)
+ (vz rotation)
+ raw-id))
+
+ (sqlite:last-insert-rowid db))
+
+(defun get-database-filename ()
+ "Return a database filename with a timestamp."
+ (asdf:system-relative-pathname :racebox-tools
+ (format nil "databases/racebox-~a.db" (local-time:now))))
A databases/.gitignore => databases/.gitignore +1 -0
M dbus.lisp => dbus.lisp +1 -0
@@ 67,6 67,7 @@
"6e400003-b5a3-f393-e0a9-e50e24dcca9e"))
+
;; (dbus:define-dbus-object racebox-service
;; (:path "/org/bluez/hci0/dev_D2_D6_A3_84_35_29/service000b/char000e"))
;; (dbus:define-dbus-signal-handler (racebox-service (wat))
M main.lisp => main.lisp +21 -7
@@ 16,12 16,26 @@
(in-package :racebox-tools)
-
-
(defun main (args)
(declare (ignorable args))
- (connect)
- (inspect (read-current-value))
- (disconnect)
- ;; TODO: What should main do?
- 0)
+ (let ((db-name (get-database-filename)))
+ (format t "db-name: ~a~%" db-name)
+ (sqlite:with-open-database (db db-name)
+ (create-db db))
+ (unwind-protect
+ (handler-case
+ (progn
+ (connect)
+ (loop
+ :do
+ (sqlite:with-open-database (db db-name)
+ (let* ((raw-value (read-current-value))
+ (gps-value (to-gps-message raw-value)))
+ (with-slots (raw-id) gps-value
+ (setf raw-id (insert-message db raw-value))
+ (insert-message db gps-value))))
+ (sleep (/ 1 25))))
+ (error (err)
+ (format t "Received error: ~a~%Quitting GPS logger.~%~%" err)))
+ (disconnect))
+ 0))
M messages.lisp => messages.lisp +2 -1
@@ 214,7 214,8 @@
(speed 0.0 :type real)
(heading 0.0 :type real)
(g-force (vec3 0.0 0.0 0.0) :type vec3)
- (rotation (vec3 0.0 0.0 0.0) :type vec3))
+ (rotation (vec3 0.0 0.0 0.0) :type vec3)
+ (raw-id 0 :type fixnum))
(defun to-gps-message (data-message)
M package.lisp => package.lisp +4 -0
@@ 92,6 92,10 @@
#:gps-message-g-force
#:gps-message-rotation
+ #:create-db
+ #:get-data-base-filename
+ #:insert-message
+ #:main
;; Not yet implemented...
;; #:read-csv-stream
;; #:read-csv-file
A racebox-recorder.service => racebox-recorder.service +23 -0
@@ 0,0 1,23 @@
+[Unit]
+Description=RaceBox Mini Recorder
+
+After=bluetooth.target
+StartLimitIntervalSec=0
+
+[Service]
+Type=simple
+Restart=always
+RestartSec=1
+
+# TODO: What's the best practice on choosing a user?
+User=racebox
+
+ExecStart=/usr/bin/env sbcl --non-interactive --no-userinit \
+ --eval "(let ((quicklisp-init \"$HOME/quicklisp/setup.lisp\")) \
+ (when (probe-file quicklisp-init) \
+ (load quicklisp-init)))" \
+ --eval "(ql:quickload :racebox-tools)" \
+ --eval "(main nil)"
+
+[Install]
+WantedBy=multi-user.target<
\ No newline at end of file
M racebox-tools.asd => racebox-tools.asd +3 -0
@@ 29,15 29,18 @@
#:dbus
#:local-time
#:local-time-duration
+ #:utm
#:sqlite
#:dbus-tools
#:3d-vectors
+ #:uiop
)
:components ((:file "package")
(:file "dbus")
(:file "messages")
(:file "importers")
+ (:file "database")
(:file "main"))
:in-order-to ((test-op (test-op racebox-tools.test))))
A sql/create_table_gps_msg.sql => sql/create_table_gps_msg.sql +19 -0
@@ 0,0 1,19 @@
+create table if not exists gps_message(
+ timestamp datetime,
+ longitude real,
+ latitude real,
+ msl_altitude real,
+ wgs_altitude real,
+ speed real,
+ heading real,
+ g_force_x real,
+ g_force_y real,
+ g_force_z real,
+ rotation_x real,
+ rotation_y real,
+ rotation_z real,
+ raw_id integer not null,
+ FOREIGN KEY (raw_id)
+ REFERENCES raw_data (raw_msg_id)
+ ON DELETE SET NULL
+)
A sql/create_table_raw_data.sql => sql/create_table_raw_data.sql +50 -0
@@ 0,0 1,50 @@
+create table if not exists raw_data(
+ raw_msg_id integer primary key autoincrement,
+ itow integer,
+
+ year integer,
+ month integer,
+ day integer,
+
+ hour integer,
+ minute integer,
+ second integer,
+
+ validity_flags integer,
+ time_accuraccy integer,
+ nanosecond integer,
+
+ fix_status integer,
+ fix_status_flags integer,
+ date_time_flags integer,
+ number_of_svs integer,
+
+ longitude integer,
+ latitude integer,
+
+ wgs_altitude integer,
+ msl_altitude integer,
+
+ horizontal_accuracy integer,
+ vertical_accuracy integer,
+
+ speed integer,
+ heading integer,
+
+ speed_accuracy integer,
+ heading_accuracy integer,
+
+ pdop integer,
+
+ lat_lon_flags integer,
+
+ battery_status integer,
+
+ g_force_x integer,
+ g_force_y integer,
+ g_force_z integer,
+
+ rotation_rate_x integer,
+ rotation_rate_y integer,
+ rotation_rate_z integer
+)