This commit is contained in:
William Clark 2023-11-21 14:45:14 +00:00
parent 01f5651377
commit a58f3e8b5f
13 changed files with 72 additions and 178 deletions

7
Makefile Normal file
View File

@ -0,0 +1,7 @@
build:
go build main.go
run:
go run main.go
clean:
rm -rf test.db main

View File

@ -1,14 +1,16 @@
[TH7]
port = 9090
logfreq = 60
debug = true
[DB]
type = "sqlite3"
path = "test.db"
freq = 60
[Channel_2]
type = 'N'
unit = 'C'
filter.samples = 20
[Channel_3]
type = 'T'

View File

@ -5,7 +5,6 @@ import (
"fmt"
"strings"
"th7/data/config"
"th7/data/temperature"
"th7/data/thermocouple"
"github.com/spf13/viper"
@ -28,12 +27,6 @@ var (
"T": thermocouple.T,
"UV": thermocouple.None,
}
temperatureUnits = map[string]temperature.Unit{
"C": temperature.C,
"F": temperature.F,
"K": temperature.K,
"UV": temperature.None,
}
)
func getThermocoupleType(t string) (thermocouple.Type, error) {
@ -45,15 +38,6 @@ func getThermocoupleType(t string) (thermocouple.Type, error) {
}
}
func getTemperatureUnit(u string) (temperature.Unit, error) {
u = strings.ToUpper(u)
if val, ok := temperatureUnits[u]; ok {
return val, nil
} else {
return temperature.None, ErrConfigBadTemperatureUnit
}
}
func Load() (config.Config, error) {
var cfg config.Config
@ -71,14 +55,14 @@ func Load() (config.Config, error) {
v.SetDefault("TH7.port", 8080)
v.SetDefault("TH7.cache", true)
v.SetDefault("TH7.LED", true)
v.SetDefault("TH7.logfreq", 60)
v.SetDefault("TH7.debug", false)
v.SetDefault("TH7.nolog", false)
v.SetDefault("DB.freq", 60)
cfg.Board.Port = v.GetInt("TH7.port")
cfg.Board.Cache = v.GetBool("TH7.cache")
cfg.Board.Led = v.GetBool("TH7.LED")
cfg.Board.Logfreq = v.GetInt("TH7.logfreq")
cfg.Board.Logfreq = v.GetInt("DB.freq")
cfg.Board.Debug = v.GetBool("TH7.debug")
cfg.Board.NoLogging = v.GetBool("TH7.nolog")
@ -91,11 +75,12 @@ func Load() (config.Config, error) {
v.SetDefault(head+".gain", 106.8)
v.SetDefault(head+".offset", 0.0)
v.SetDefault(head+".unit", "UV")
// filter settings
v.SetDefault(head+".filter.samples", 50)
}
for i := 1; i < 8; i++ {
var c config.Channel
var unit temperature.Unit
var tc thermocouple.Type
var head = fmt.Sprintf("Channel_%d", i)
@ -113,18 +98,9 @@ func Load() (config.Config, error) {
return cfg, err
}
c.Thermo = tc
if tc == thermocouple.None {
unit = temperature.None
} else {
unit, err = getTemperatureUnit(v.GetString(head + ".unit"))
if err != nil {
fmt.Printf("%s.unit=%s\n", head, v.GetString(head+".unit"))
return cfg, err
}
}
c.Unit = unit
c.Filter.SampleSize = v.GetInt(head+".filter.samples")
cfg.Channels = append(cfg.Channels, c)
}

View File

@ -3,8 +3,8 @@ package th7
import (
"fmt"
"log"
temp_data "th7/data/temperature"
"th7/temperature"
thermo_data "th7/data/thermocouple"
"th7/filter"
"th7/thermocouple"
"time"
)
@ -15,22 +15,11 @@ const (
DurWaitRestart = 1000 * time.Millisecond
)
const (
// TODO: configurable global or per-channel sample
Samples = 50
)
// TODO: per-channel configurable filter(-stages)
func alphaBetaFilter(arr []float64, init float64) float64 {
value := init
for i := range arr {
gain := 1.0 / (float64(i + 1))
value += (gain*arr[i] - value)
}
return value
}
// Continuously sample each configured channel, apply filter, and save result
// in a mutex controlled shared data object, that can be accessed at any time
// by a web-request or similar.
func startCacheService(t *TH7Adapter) {
data := t.data
@ -39,7 +28,7 @@ func startCacheService(t *TH7Adapter) {
// init
for i := range samples {
samples[i] = make([]float64, Samples)
samples[i] = make([]float64, t.cfg.Channels[i].Filter.SampleSize)
}
duroffset := time.Duration(0) * time.Millisecond
@ -47,19 +36,17 @@ func startCacheService(t *TH7Adapter) {
for t.run {
timer_start := time.Now()
for channel := range t.cfg.Channels {
for i := 0; i < Samples; i++ {
channel_id := t.cfg.Channels[channel].Id
channel_gain := t.cfg.Channels[channel].Gain
// update Table
t.pcbPort.UpdateTable()
for k := range t.cfg.Channels {
channel_id := t.cfg.Channels[k].Id
channel_gain := t.cfg.Channels[k].Gain
val := t.pcbPort.ReadChannel(channel_id, channel_gain)
samples[k][i] = val
for i:=0; i<t.cfg.Channels[channel].Filter.SampleSize; i++ {
// update vref table
t.pcbPort.UpdateTable()
measurement := t.pcbPort.ReadChannel(channel_id, channel_gain)
samples[channel][i] = measurement
time.Sleep(DurWaitBetweenSample)
}
time.Sleep(DurWaitBetweenChannel)
@ -67,44 +54,38 @@ func startCacheService(t *TH7Adapter) {
// do alpha~beta on each channel,
// putting the value in
// TODO: in config have different types.
for i := range samples {
data[i].Value = alphaBetaFilter(samples[i], data[i].Value)
data[i].Value = filter.AlphaBetaFilter(samples[i], data[i].Value)
}
// read pcb temp and apply CJC
// the board temperature wont noticeably change for the ~300 ms of sampling
// at least compared to the TC error.
// so only reading pcb temp and applying cjc after this duration
// probably wont affect the readings.
pcb_temperature := t.pcbPort.ReadPCBTemp()
for k := range t.cfg.Channels {
tc := t.cfg.Channels[k].Thermo
data[k].Unit = t.cfg.Channels[k].Unit
thermo_type := t.cfg.Channels[k].Thermo
data[k].Id = t.cfg.Channels[k].Id
// if we can apply CJC with this configuration ...
if thermocouple.WithinTemperatureRange(tc, pcb_temperature) {
cjc_uv, err := thermocouple.UV(tc, pcb_temperature)
if thermocouple.WithinTemperatureRange(thermo_type, pcb_temperature) {
cjc_uv, err := thermocouple.UV(thermo_type, pcb_temperature)
if err != nil {
log.Println("Error doing CJC", err)
cjc_uv = 0.0
}
data[k].Value += cjc_uv
}
// CJC can not be applied
// board temp out of range for specified thermocouple type
// perhaps just subtract board temperature? dont know
//fmt.Println("No valid CJC for Channel", t.cfg.Channels[k].Id)
// if the temperature unit is "None", it is probably for UV only.
// if the thermocouple unit is "None", it is for UV only.
// so do not convert it to a value using t/c polys
if data[k].Unit != temp_data.None {
new_value_c, err := thermocouple.C(tc, data[k].Value)
if t.cfg.Channels[k].Thermo != thermo_data.None {
new_value_c, err := thermocouple.C(thermo_type, data[k].Value)
if err != nil {
log.Println("Error converting UV to C", t.cfg.Channels[k].Id, data[k].Value)
new_value_c = data[k].Value
@ -112,21 +93,7 @@ func startCacheService(t *TH7Adapter) {
data[k].Value = new_value_c
// It is celsius by default
switch data[k].Unit {
case temp_data.F:
data[k].Value = temperature.F(temp_data.C, data[k].Value)
case temp_data.K:
data[k].Value = temperature.K(temp_data.C, data[k].Value)
}
// temp conv if any is applied ... add offset
// offset is specified in the same temperature unit
// as is specified for the channel
// ie if unit = F then the offset counts in unit F also.
data[k].Value += t.cfg.Channels[k].Offset
}
@ -149,7 +116,12 @@ func startCacheService(t *TH7Adapter) {
elapsed := time.Since(timer_start)
fmt.Printf("updated %d channels, %d samples each (total=%d) dur=%s\n", len(samples), Samples, len(samples)*Samples, elapsed)
total_sample_size := 0
for k := range t.cfg.Channels {
total_sample_size += t.cfg.Channels[k].Filter.SampleSize
}
fmt.Printf("updated %d channels. %d samples. dur=%s\n", len(t.cfg.Channels), total_sample_size, elapsed)
if elapsed >= (DurWaitRestart + duroffset) {
fmt.Println("timer overrun, adding 100ms")

View File

@ -1,16 +1,19 @@
package config
import (
"th7/data/temperature"
"th7/data/thermocouple"
)
type Filter struct {
SampleSize int `json:"sample_size"`
}
type Channel struct {
Id int `json:"id"`
Thermo thermocouple.Type `json:"thermocouple"`
Gain float64 `json:"gain"`
Offset float64 `json:"offset"`
Unit temperature.Unit `json:"unit"`
Filter Filter `json:"filter"`
}
type TH7 struct {

View File

@ -1,11 +1,9 @@
package core
import "th7/data/temperature"
type Channel struct {
Id int `json:"id"`
Value float64 `json:"value"`
Unit temperature.Unit `json:"unit"`
}
type Ratio struct {

View File

@ -1,10 +0,0 @@
package temperature
type Unit int
const (
None Unit = iota // None is UV
K
C
F
)

View File

@ -19,7 +19,7 @@ func (d *DummyAdapter) Close() {
func (d *DummyAdapter) Save(channels []core.Channel) error {
fmt.Println("=======================================")
for _, val := range channels {
fmt.Printf("Saved: id=%d, unit=%d, value=%g\n", val.Id, val.Unit, val.Value)
fmt.Printf("Saved: id=%d, value=%g\n", val.Id, val.Value)
}
fmt.Println("=======================================")
return nil

12
filter/filter.go Normal file
View File

@ -0,0 +1,12 @@
package filter
func AlphaBetaFilter(arr []float64, init float64) float64 {
value := init
for i := range arr {
gain := 1.0 / (float64(i + 1))
value += (gain*arr[i] - value)
}
return value
}

View File

@ -1,7 +1,6 @@
package main
import (
"fmt"
"log"
"os"
"os/signal"
@ -34,8 +33,6 @@ func main() {
log.Fatalf("Error loading config: %v", err)
}
fmt.Println(config)
pcbPort, err = pcb.NewAdapter(config)
if err != nil {
log.Fatalf("Fatal error: %v\n", err)

View File

@ -2,7 +2,9 @@ package pcb
import (
"fmt"
"math/rand"
"th7/data/pcb"
"time"
)
type DummyAdapter struct {
@ -10,6 +12,7 @@ type DummyAdapter struct {
}
func NewDummyAdapter() (*DummyAdapter, error) {
rand.Seed(time.Now().UnixNano())
return &DummyAdapter{}, nil
}
@ -29,7 +32,7 @@ func (d *DummyAdapter) GetTable() pcb.Table {
}
func (d *DummyAdapter) ReadChannel(id int, gain float64) float64 {
return -9000.0
return -9000.0 * rand.Float64()
}
func (d *DummyAdapter) ReadPCBTemp() float64 {

View File

@ -1,68 +0,0 @@
package temperature
import (
"th7/data/temperature"
)
func c_to_k(val float64) float64 {
return val + 273.15
}
func c_to_f(val float64) float64 {
return (val * 9 / 5) + 32.0
}
func f_to_c(val float64) float64 {
return (val - 32.0) * 5 / 9
}
func f_to_k(val float64) float64 {
return (val-32.0)*5/9 + 273.15
}
func k_to_c(val float64) float64 {
return val - 273.15
}
func k_to_f(val float64) float64 {
return (val-273.15)*9/5 + 32.0
}
func C(unit temperature.Unit, value float64) float64 {
switch unit {
case temperature.C:
return value
case temperature.F:
return c_to_f(value)
case temperature.K:
return c_to_k(value)
}
return 0
}
func F(unit temperature.Unit, value float64) float64 {
switch unit {
case temperature.F:
return value
case temperature.C:
return f_to_c(value)
case temperature.K:
return f_to_k(value)
}
return 0
}
func K(unit temperature.Unit, value float64) float64 {
switch unit {
case temperature.K:
return value
case temperature.F:
return k_to_f(value)
case temperature.C:
return k_to_c(value)
}
return 0
}

View File

@ -95,8 +95,10 @@ func NewGinAdapter(corePort ports.CorePort, port int) *GinAdapter {
adapter.corePort = corePort
adapter.port = port
adapter.router = gin.Default()
//adapter.router = gin.Default()
adapter.router = gin.New()
// API
adapter.router.GET("/ratio", adapter.RatioHandler)
adapter.router.GET("/channels", adapter.ChannelsHandler)
adapter.router.GET("/channel/:id", adapter.ChannelByIDHandler)