Skip to content

Commit be21b35

Browse files
committed
Switch to generic sensor driver and remove FakeGPS driver due to Google API necessity
1 parent 968b467 commit be21b35

15 files changed

Lines changed: 591 additions & 52 deletions

File tree

‎build.gradle‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ dependencies {
4141
testImplementation 'org.sensorhub:sensorhub-service-consys:' + oshCoreVersion
4242

4343
testImplementation project(':sensorhub-driver-ffmpeg')
44-
testImplementation project(':sensorhub-driver-fakegps')
44+
testImplementation project(':sensorhub-driver-template')
4545
testImplementation project(':sensorhub-driver-fakeweather')
4646

4747

@@ -72,7 +72,7 @@ test {
7272
maxParallelForks = 1
7373
}
7474

75-
project(':sensorhub-driver-fakegps') {
75+
project(':sensorhub-driver-template') {
7676
tasks.named('test') {
7777
enabled = false
7878
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# [NAME]
2+
3+
Sensor adapter for [NAME].
4+
5+
## Configuration
6+
7+
Configuring the sensor requires:
8+
Select ```Sensors``` from the left-hand accordion control and
9+
right-click for the context-sensitive menu in the accordion control.
10+
11+
- **Module Name:** A name for the instance of the driver
12+
- **Serial Number:** The platform's serial number, or a unique identifier
13+
- **Auto Start:** Check the box to start this module when OSH node is launched
14+
15+
Storage:
16+
Select ```Storage``` from the left-hand accordion control and
17+
right-click for the context-sensitive menu in the accordion control.
18+
Use a ```Real-Time Stream Storage Module``` providing the sensor module as the
19+
20+
- **Data Source ID:** Select the identifier for the storage module created in the configuring sensor step,
21+
use looking glass to select it from the list of know sensor modules
22+
- **Auto Start:** Check the box to start this module when OSH node is launched
23+
- **Process Events:** Check the box if you want events to be stored as new records
24+
25+
And then configure the
26+
27+
- **Storage Config** using a ```Perst Record Storage``` instance providing the
28+
- **Storage Path** as the location where the OSH records are to be stored.
29+
30+
SOS Service:
31+
Select ```Services``` from the left-hand accordion control, then Offerings, then the **+**
32+
symbol to add a new offering.
33+
Provide the following:
34+
35+
- **Name:** A name for the offering
36+
- **Description:** A description of the offering
37+
- **StorageId:** Select the identifier for the storage module created in the previous step,
38+
use looking glass to select it from the list of know storage modules
39+
- **SensorId:** Select the identifier for the storage module created in the configuring sensor step,
40+
use looking glass to select it from the list of known sensor modules
41+
- **Enable:** Check the box to enable this offering
42+
43+
## Sample Requests
44+
45+
The following are a list of example requests and their respective responses.
46+
The **IP ADDRESS** and **PORT** will need to be specified and point to the instance
47+
of the OpenSensorHub node serving the data.
48+
49+
### [Observed Property] Request
50+
51+
- **HTTP**
52+
- http://[IP ADDRESS]:[PORT]/sensorhub/sos?service=SOS&version=2.0&request=GetResult&offering=[URN]
53+
&observedProperty=[OBSERVED_PROPERTY]&temporalFilter=phenomenonTime,[START_TIME]/[END_TIME]
54+
&replaySpeed=1&responseFormat=application/json
55+
56+
Response:
57+
58+
```
59+
60+
```
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
description = 'Put your description here'
2+
ext.details = "Details here"
3+
version = '1.0.0'
4+
5+
dependencies {
6+
implementation 'org.sensorhub:sensorhub-core:' + oshCoreVersion
7+
// implementation project(':sensorhub-service-video')
8+
// implementation project(':sensorhub-driver-videocam')
9+
testImplementation('junit:junit:4.13.1')
10+
}
11+
12+
// exclude tests requiring connection to the sensor
13+
// these have to be run manually
14+
// If tests are to be excluded list them here as follows
15+
// exclude '**/TestNameClass.class'
16+
test {
17+
useJUnit()
18+
}
19+
20+
// add info to OSGi manifest
21+
osgi {
22+
manifest {
23+
attributes ('Bundle-Vendor': 'Botts Inc')
24+
attributes ('Bundle-Activator': 'com.sample.impl.sensor.drivername.Activator')
25+
}
26+
}
27+
28+
// add info to maven pom
29+
ext.pom >>= {
30+
developers {
31+
developer {
32+
id 'some id'
33+
name 'Your name'
34+
organization ''
35+
organizationUrl ''
36+
}
37+
}
38+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/***************************** BEGIN LICENSE BLOCK ***************************
2+
The contents of this file are subject to the Mozilla Public License, v. 2.0.
3+
If a copy of the MPL was not distributed with this file, You can obtain one
4+
at http://mozilla.org/MPL/2.0/.
5+
6+
Software distributed under the License is distributed on an "AS IS" basis,
7+
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
8+
for the specific language governing rights and limitations under the License.
9+
10+
Copyright (C) 2020-2025 Botts Innovative Research, Inc. All Rights Reserved.
11+
******************************* END LICENSE BLOCK ***************************/
12+
package com.sample.impl.sensor.drivername;
13+
14+
import org.sensorhub.utils.OshBundleActivator;
15+
16+
/**
17+
* The presence of this class tells the OpenSensorHub OSGI machinery about this module.
18+
* It is referenced in the 'Bundle-Activator' attribute in the OSGI section of the build.gradle file.
19+
*/
20+
@SuppressWarnings("unused")
21+
public class Activator extends OshBundleActivator {
22+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/***************************** BEGIN LICENSE BLOCK ***************************
2+
The contents of this file are subject to the Mozilla Public License, v. 2.0.
3+
If a copy of the MPL was not distributed with this file, You can obtain one
4+
at http://mozilla.org/MPL/2.0/.
5+
6+
Software distributed under the License is distributed on an "AS IS" basis,
7+
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
8+
for the specific language governing rights and limitations under the License.
9+
10+
Copyright (C) 2020-2025 Botts Innovative Research, Inc. All Rights Reserved.
11+
******************************* END LICENSE BLOCK ***************************/
12+
package com.sample.impl.sensor.drivername;
13+
14+
import org.sensorhub.api.config.DisplayInfo;
15+
import org.sensorhub.api.sensor.SensorConfig;
16+
17+
/**
18+
* Configuration settings for the {@link Sensor} driver exposed via the OpenSensorHub Admin panel.
19+
* <p>
20+
* Public fields are exposed in the Admin panel for configuration by the user.
21+
* These fields can be annotated with the DisplayInfo annotation to provide additional information to the user
22+
* or to restrict the values that can be entered.
23+
* <p>
24+
* Configuration takes the form of:
25+
* <pre>{@code
26+
* @DisplayInfo(label = "Field Label", desc = "A description of the field to be shown in the UI.")
27+
* public Type configOption = "default value";
28+
* }</pre>
29+
*/
30+
public class Config extends SensorConfig {
31+
/**
32+
* The unique identifier for the configured sensor (or sensor platform).
33+
*/
34+
@DisplayInfo.Required
35+
@DisplayInfo(desc = "Serial number or unique identifier")
36+
public String serialNumber = "sensor001";
37+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/***************************** BEGIN LICENSE BLOCK ***************************
2+
The contents of this file are subject to the Mozilla Public License, v. 2.0.
3+
If a copy of the MPL was not distributed with this file, You can obtain one
4+
at http://mozilla.org/MPL/2.0/.
5+
6+
Software distributed under the License is distributed on an "AS IS" basis,
7+
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
8+
for the specific language governing rights and limitations under the License.
9+
10+
Copyright (C) 2020-2025 Botts Innovative Research, Inc. All Rights Reserved.
11+
******************************* END LICENSE BLOCK ***************************/
12+
package com.sample.impl.sensor.drivername;
13+
14+
import org.sensorhub.api.module.IModule;
15+
import org.sensorhub.api.module.IModuleProvider;
16+
import org.sensorhub.api.module.ModuleConfig;
17+
import org.sensorhub.impl.module.JarModuleProvider;
18+
19+
/**
20+
* Descriptor classes provide access to informative data on the OpenSensorHub driver.
21+
*/
22+
public class Descriptor extends JarModuleProvider implements IModuleProvider {
23+
/**
24+
* Retrieves the class implementing the OpenSensorHub interface necessary to perform SOS/SPS/SOS-T operations.
25+
*
26+
* @return The class used to interact with the sensor/sensor platform.
27+
*/
28+
@Override
29+
public Class<? extends IModule<?>> getModuleClass() {
30+
return Sensor.class;
31+
}
32+
33+
/**
34+
* Identifies the class used to configure this driver.
35+
*
36+
* @return The java class used to exposing configuration settings for the driver.
37+
*/
38+
@Override
39+
public Class<? extends ModuleConfig> getModuleConfigClass() {
40+
return Config.class;
41+
}
42+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/***************************** BEGIN LICENSE BLOCK ***************************
2+
The contents of this file are subject to the Mozilla Public License, v. 2.0.
3+
If a copy of the MPL was not distributed with this file, You can obtain one
4+
at http://mozilla.org/MPL/2.0/.
5+
6+
Software distributed under the License is distributed on an "AS IS" basis,
7+
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
8+
for the specific language governing rights and limitations under the License.
9+
10+
Copyright (C) 2020-2025 Botts Innovative Research, Inc. All Rights Reserved.
11+
******************************* END LICENSE BLOCK ***************************/
12+
package com.sample.impl.sensor.drivername;
13+
14+
import net.opengis.swe.v20.DataBlock;
15+
import net.opengis.swe.v20.DataComponent;
16+
import net.opengis.swe.v20.DataEncoding;
17+
import net.opengis.swe.v20.DataRecord;
18+
import org.sensorhub.api.data.DataEvent;
19+
import org.sensorhub.impl.sensor.AbstractSensorOutput;
20+
import org.vast.swe.helper.GeoPosHelper;
21+
22+
import java.util.ArrayList;
23+
24+
/**
25+
* Output specification and provider for {@link Sensor}.
26+
*/
27+
public class Output extends AbstractSensorOutput<Sensor> {
28+
static final String SENSOR_OUTPUT_NAME = "SensorOutput";
29+
static final String SENSOR_OUTPUT_LABEL = "Sensor Output";
30+
static final String SENSOR_OUTPUT_DESCRIPTION = "Sensor output data";
31+
32+
private static final int MAX_NUM_TIMING_SAMPLES = 10;
33+
34+
private final ArrayList<Double> intervalHistogram = new ArrayList<>(MAX_NUM_TIMING_SAMPLES);
35+
private final Object histogramLock = new Object();
36+
private final Object processingLock = new Object();
37+
38+
private DataRecord dataRecord;
39+
private DataEncoding dataEncoding;
40+
41+
/**
42+
* Creates a new output for the sensor driver.
43+
*
44+
* @param parentSensor Sensor driver providing this output.
45+
*/
46+
Output(Sensor parentSensor) {
47+
super(SENSOR_OUTPUT_NAME, parentSensor);
48+
}
49+
50+
/**
51+
* Initializes the data structure for the output, defining the fields, their ordering, and data types.
52+
*/
53+
void doInit() {
54+
// Get an instance of SWE Factory suitable to build components
55+
GeoPosHelper sweFactory = new GeoPosHelper();
56+
57+
// Create the data record description
58+
dataRecord = sweFactory.createRecord()
59+
.name(SENSOR_OUTPUT_NAME)
60+
.label(SENSOR_OUTPUT_LABEL)
61+
.description(SENSOR_OUTPUT_DESCRIPTION)
62+
.addField("sampleTime", sweFactory.createTime()
63+
.asSamplingTimeIsoUTC()
64+
.label("Sample Time")
65+
.description("Time of data collection"))
66+
.addField("data", sweFactory.createText()
67+
.label("Example Data"))
68+
.build();
69+
70+
dataEncoding = sweFactory.newTextEncoding(",", "\n");
71+
}
72+
73+
@Override
74+
public DataComponent getRecordDescription() {
75+
return dataRecord;
76+
}
77+
78+
@Override
79+
public DataEncoding getRecommendedEncoding() {
80+
return dataEncoding;
81+
}
82+
83+
@Override
84+
public double getAverageSamplingPeriod() {
85+
synchronized (histogramLock) {
86+
double sum = 0;
87+
for (double sample : intervalHistogram)
88+
sum += sample;
89+
90+
return sum / intervalHistogram.size();
91+
}
92+
}
93+
94+
/**
95+
* Sets the data for the output and publishes it.
96+
*/
97+
public void setData(long timestamp, String data) {
98+
synchronized (processingLock) {
99+
DataBlock dataBlock = latestRecord == null ? dataRecord.createDataBlock() : latestRecord.renew();
100+
101+
updateIntervalHistogram();
102+
103+
// Populate the data block
104+
dataBlock.setDoubleValue(0, timestamp / 1000d);
105+
dataBlock.setStringValue(1, data);
106+
107+
// Publish the data block
108+
latestRecord = dataBlock;
109+
latestRecordTime = timestamp;
110+
eventHandler.publish(new DataEvent(latestRecordTime, Output.this, dataBlock));
111+
}
112+
}
113+
114+
/**
115+
* Updates the interval histogram with the time between the latest record and the current time
116+
* for calculating the average sampling period.
117+
*/
118+
private void updateIntervalHistogram() {
119+
synchronized (histogramLock) {
120+
if (latestRecord != null && latestRecordTime != Long.MIN_VALUE) {
121+
long interval = System.currentTimeMillis() - latestRecordTime;
122+
intervalHistogram.add(interval / 1000d);
123+
124+
if (intervalHistogram.size() > MAX_NUM_TIMING_SAMPLES) {
125+
intervalHistogram.remove(0);
126+
}
127+
}
128+
}
129+
}
130+
}

0 commit comments

Comments
 (0)