@em-foundation/emscope 25.1.0 → 25.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/dist/emscope +187 -77
  3. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,4 +1,18 @@
1
- ## VERSION-25-0.2
1
+ ## VERSION-25-3.0
2
+
3
+ * aligned with new `BlueJoule/capture` directory structure
4
+ * streamlined generation of `ABOUT.md` with automatic HW/SW inclusion
5
+ * one digit of precision after the decimal point in most results
6
+
7
+ ## VERSION-25-2.0
8
+
9
+ * new `emscope grab -p, --ppk-supply` option
10
+ * new `emscope scan --refresh` option
11
+ * adds capture_time to generated .jls file
12
+ * minor bug fixes
13
+ * works with Joulescope UI 1.3.9
14
+
15
+ ## VERSION-25-1.0
2
16
 
3
17
  * uses `BlueJoule` repo as exemplar
4
18
  * updated `README` documentation
package/dist/emscope CHANGED
@@ -12323,6 +12323,7 @@ var Capture = class _Capture {
12323
12323
  "duration",
12324
12324
  "sampling_rate",
12325
12325
  "sample_count",
12326
+ "version",
12326
12327
  "voltage"
12327
12328
  ];
12328
12329
  static #SAVE_KEYS = [
@@ -12335,6 +12336,7 @@ var Capture = class _Capture {
12335
12336
  cap._rootdir = rootdir;
12336
12337
  cap._duration = duration;
12337
12338
  cap._device = device;
12339
+ cap._version = version();
12338
12340
  cap._voltage = voltage;
12339
12341
  cap._creation_date = /* @__PURE__ */ new Date();
12340
12342
  cap._sampling_rate = _Capture.#SAMPLING_RATE.get(device) ?? 0;
@@ -12612,9 +12614,6 @@ var Window = class {
12612
12614
  return this.#off >= 0 && this.#off + this.#wid <= this.#sig.data.length;
12613
12615
  }
12614
12616
  };
12615
- function amps(val) {
12616
- return toEng(val, "A");
12617
- }
12618
12617
  function fail(msg, cond = true) {
12619
12618
  if (cond) {
12620
12619
  console.log(`*** ${msg} ***`);
@@ -12625,7 +12624,7 @@ function infoMsg(msg) {
12625
12624
  console.log(`${TAB}${msg}`);
12626
12625
  }
12627
12626
  function joules(j) {
12628
- return toEng(j, "J");
12627
+ return toEng(j, "J", 0);
12629
12628
  }
12630
12629
  function parseHms(s) {
12631
12630
  if (/^\d+$/.test(s)) return Number(s);
@@ -12643,12 +12642,18 @@ function secsToHms(total) {
12643
12642
  const pad = (n) => n.toString().padStart(2, "0");
12644
12643
  return `${pad(h)}:${pad(m)}:${pad(s)}`;
12645
12644
  }
12646
- function toEng(x, u) {
12645
+ function toEng(x, u, e) {
12647
12646
  if (x == 0) return `0 ${u}`;
12648
- const exp = Math.floor(Math.log10(Math.abs(x)) / 3) * 3;
12647
+ const exp = e ?? Math.floor(Math.log10(Math.abs(x)) / 3) * 3;
12649
12648
  const mantissa = x / 10 ** exp;
12650
12649
  const unit = { [-9]: ` n${u}`, [-6]: ` \xB5${u}`, [-3]: ` m${u}`, [0]: ` ${u}`, [3]: ` k${u}` }[exp] || `e${exp} ${u}`;
12651
- return `${mantissa.toFixed(3).padStart(7, " ")}${unit}`;
12650
+ return `${mantissa.toFixed(1).padStart(4, " ")}${unit}`;
12651
+ }
12652
+ function uAmps(val) {
12653
+ return toEng(val, "A", -6);
12654
+ }
12655
+ function uJoules(j) {
12656
+ return toEng(j, "J", -6);
12652
12657
  }
12653
12658
  function version() {
12654
12659
  const path = import_path.default.resolve(__dirname, "..", "package.json");
@@ -12686,10 +12691,28 @@ ${dpath}:`);
12686
12691
  // src/Detecter.ts
12687
12692
  function exec(opts) {
12688
12693
  const cap = Capture.load(opts.capture);
12689
- const aobj = analyze(cap, opts.trim, opts.gap, opts.minDuration, opts.minEnergy);
12694
+ let params = {};
12695
+ if (opts.refresh) {
12696
+ if (cap.analysis) {
12697
+ for (const opt of cap.analysis?.options) {
12698
+ const a = opt.split(" ");
12699
+ const [pname, pval] = [a[0].slice(2), Number(a[1])];
12700
+ params[pname] = Number(pval);
12701
+ }
12702
+ if (Number.isNaN(params.trim)) {
12703
+ params.trim = cap.analysis.events.length;
12704
+ }
12705
+ }
12706
+ } else {
12707
+ params.gap = opts.gap;
12708
+ params.min_dur = opts.minDuration;
12709
+ params.min_egy = opts.minEnergy;
12710
+ params.trim = opts.trim;
12711
+ }
12712
+ const aobj = analyze(cap, params);
12690
12713
  cap.bind(aobj);
12691
12714
  }
12692
- function analyze(cap, trim, gap, min_dur, min_egy) {
12715
+ function analyze(cap, params = {}) {
12693
12716
  infoMsg("analyzing captured data...");
12694
12717
  const rsig = cap.current_sig;
12695
12718
  const width = rsig.secsToOff(25e-5);
@@ -12717,26 +12740,26 @@ function analyze(cap, trim, gap, min_dur, min_egy) {
12717
12740
  }
12718
12741
  }
12719
12742
  let options = new Array();
12720
- if (gap !== void 0) {
12721
- markers = combineMarkers(rsig, markers, rsig.secsToOff(gap / 1e3));
12722
- options.push(`--gap ${gap}`);
12743
+ if (params.gap !== void 0) {
12744
+ markers = combineMarkers(rsig, markers, rsig.secsToOff(params.gap / 1e3));
12745
+ options.push(`--gap ${params.gap}`);
12723
12746
  }
12724
- if (min_dur != void 0) {
12725
- const min_wid = rsig.secsToOff(min_dur / 1e3);
12747
+ if (params.min_dur != void 0) {
12748
+ const min_wid = rsig.secsToOff(params.min_dur / 1e3);
12726
12749
  markers = markers.filter((m) => m.width >= min_wid);
12727
- options.push(`--min-duration ${min_dur}`);
12750
+ options.push(`--min-duration ${params.min_dur}`);
12728
12751
  }
12729
- if (min_egy != void 0) {
12730
- markers = markers.filter((m) => cap.energyWithin(m) >= min_egy / 1e6);
12731
- options.push(`--min-energy ${min_egy}`);
12752
+ if (params.min_egy != void 0) {
12753
+ markers = markers.filter((m) => cap.energyWithin(m) >= params.min_egy / 1e6);
12754
+ options.push(`--min-energy ${params.min_egy}`);
12732
12755
  }
12733
12756
  let span = rsig.window(rsig.data.length).toMarker();
12734
- if (trim) {
12735
- [span, markers] = trimEvents(cap, markers, trim);
12736
- options.push(`--trim ${trim}`);
12757
+ if (params.trim) {
12758
+ [span, markers] = trimEvents(cap, markers, params.trim);
12759
+ options.push(`--trim ${params.trim}`);
12737
12760
  }
12738
12761
  infoMsg(`found ${markers.length} event(s)`);
12739
- return { span, events: markers, sleep: si, options };
12762
+ return { span, events: markers, sleep: si, options, version: version() };
12740
12763
  }
12741
12764
  function combineMarkers(sig, markers, gap) {
12742
12765
  let res = new Array();
@@ -12797,6 +12820,7 @@ function trimEvents(cap, markers, count) {
12797
12820
  }
12798
12821
 
12799
12822
  // src/AboutFile.ts
12823
+ var ChildProc = __toESM(require("child_process"));
12800
12824
  var Fs3 = __toESM(require("fs"));
12801
12825
  var Path3 = __toESM(require("path"));
12802
12826
  var TAG = "@emscope-pack";
@@ -12805,25 +12829,10 @@ var END = `<!-- ${TAG}:end -->`;
12805
12829
  var RE = /<!--\s*@emscope-pack:start\s*-->[\s\S]*?<!--\s*@emscope-pack:end\s*-->/m;
12806
12830
  function update(capdir) {
12807
12831
  const cap = Capture.load(capdir);
12808
- const TXT = `
12809
- <h1 align="center">Hardware Platform \xB7 Software Environment</h1>
12810
-
12811
- ## HW/SW configuration
12812
-
12813
- ## EM&bull;Scope results
12832
+ const TXT = `<h1 align="center">Hardware Platform \xB7 Software Environment \xB7 xVx</h1>
12814
12833
 
12815
12834
  ${START}
12816
-
12817
12835
  ${END}
12818
-
12819
- ## Typical event
12820
-
12821
- <p align="center">
12822
- <img src="event-ID.png" alt="Event" width="900">
12823
- </p>
12824
-
12825
- ## Observations
12826
-
12827
12836
  `;
12828
12837
  const file = Path3.join(cap.rootdir, "ABOUT.md");
12829
12838
  if (!Fs3.existsSync(file)) {
@@ -12831,10 +12840,7 @@ ${END}
12831
12840
  }
12832
12841
  const src = Fs3.readFileSync(file, "utf-8");
12833
12842
  const gen = mkGen(cap);
12834
- const block = `${START}
12835
-
12836
- ${gen}
12837
-
12843
+ const block = `${START}${gen}
12838
12844
  ${END}`;
12839
12845
  const out = RE.test(src) ? src.replace(RE, block) : `${src.replace(/\s*$/, "")}
12840
12846
 
@@ -12842,8 +12848,26 @@ ${block}
12842
12848
  `;
12843
12849
  Fs3.writeFileSync(file, out);
12844
12850
  }
12851
+ function getBuildDir(cap) {
12852
+ const bn = Path3.basename(cap.rootdir).split("-")[0];
12853
+ return Path3.join(Path3.dirname(cap.rootdir), bn);
12854
+ }
12855
+ function getEvtId(cap) {
12856
+ for (const fn of Fs3.readdirSync(cap.rootdir)) {
12857
+ const m = fn.match(/^event\-([A-Z])\.png$/);
12858
+ if (m) return m[1];
12859
+ }
12860
+ fail(`no 'event-ID.png' file found`);
12861
+ return "";
12862
+ }
12845
12863
  function mkGen(cap) {
12846
- const aobj = cap.analysis ?? analyze(cap);
12864
+ fail(`no prior analysis: run 'emscope scan ...'`, cap.analysis === void 0);
12865
+ const brd = Path3.basename(Path3.dirname(cap.rootdir));
12866
+ const brd_txt = readBrdTxt(brd);
12867
+ const bld_dir = getBuildDir(cap);
12868
+ const bld_txt = readBldTxt(bld_dir);
12869
+ const eid = getEvtId(cap);
12870
+ const aobj = cap.analysis;
12847
12871
  const si = aobj.sleep;
12848
12872
  const sl_v = cap.avg_voltage;
12849
12873
  const sl_avg = aobj.sleep.avg;
@@ -12856,31 +12880,83 @@ function mkGen(cap) {
12856
12880
  const egy10_s = sl_pwr * 10 + egy1_e;
12857
12881
  const egy10_d = egy10_s * 86400 / 10;
12858
12882
  const ems10 = 80 / egy10_d;
12859
- const date = (/* @__PURE__ */ new Date()).toISOString();
12883
+ const cap_date = mkTimestamp(cap.creation_date);
12884
+ const gen_date = mkTimestamp(/* @__PURE__ */ new Date());
12860
12885
  const GEN = `
12886
+
12887
+ <!-- *** AUTOMATICALLY GENERATED CONTENT \u2013 DO NOT EDIT *** -->
12888
+
12889
+ <p align="right"><sub>captured on ${cap_date}<br>generated on ${gen_date}</sub></p>
12890
+
12891
+ ## HW/SW Configuration
12892
+
12893
+ ${brd_txt}
12894
+ ${bld_txt}
12895
+
12896
+
12897
+ ## EM&bull;Scope results \xB7 ${cap.device}
12898
+
12861
12899
  ### \u{1F7E0}&ensp;sleep
12862
12900
 
12863
12901
  | supply voltage | &emsp;current (avg)&emsp; | &emsp;current (std)&emsp; | &emsp;average power&emsp;
12864
12902
  |:---:|:---:|:---:|:---:|
12865
- | ${sl_v.toFixed(2)} V | ${amps(sl_avg)} | ${amps(sl_std)} | ${toEng(sl_pwr, "W")} |
12903
+ | ${sl_v.toFixed(1)} V | ${uAmps(sl_avg)} | ${uAmps(sl_std)} | ${toEng(sl_pwr, "W")} |
12866
12904
 
12867
12905
  ### \u{1F7E0}&ensp;1&thinsp;s event period
12868
12906
 
12869
12907
  | &emsp;&emsp;event energy (avg)&emsp;&emsp; | &emsp;&emsp;energy per period&emsp;&emsp; | &emsp;&emsp;energy per day&emsp;&emsp; | &emsp;&emsp;&emsp;**EM&bull;eralds**&emsp;&emsp;&emsp;
12870
12908
  |:---:|:---:|:---:|:---:|
12871
- | ${joules(egy1_e)} | ${joules(egy1_s)} | ${joules(egy1_d)} | ${ems1.toFixed(2)} |
12909
+ | ${uJoules(egy1_e)} | ${uJoules(egy1_s)} | ${joules(egy1_d)} | ${ems1.toFixed(2)} |
12872
12910
 
12873
12911
  ### \u{1F7E0}&ensp;10&thinsp;s event period
12874
12912
 
12875
12913
  | &emsp;&emsp;event energy (avg)&emsp;&emsp; | &emsp;&emsp;energy per period&emsp;&emsp; | &emsp;&emsp;energy per day&emsp;&emsp; | &emsp;&emsp;&emsp;**EM&bull;eralds**&emsp;&emsp;&emsp;
12876
12914
  |:---:|:---:|:---:|:---:|
12877
- | ${joules(egy1_e)} | ${joules(egy10_s)} | ${joules(egy10_d)} | ${ems10.toFixed(2)} |
12915
+ | ${uJoules(egy1_e)} | ${uJoules(egy10_s)} | ${joules(egy10_d)} | ${ems10.toFixed(2)} |
12916
+
12917
+ ## Typical Event
12918
+
12919
+ <p align="center"><img src="event-${eid}.png" alt="Event" width="900"></p>
12878
12920
 
12879
- <br>
12880
- <p align="right"><sub>generated at ${date}</sub></p>
12881
- `;
12921
+ ## Notes
12922
+ `;
12882
12923
  return GEN;
12883
12924
  }
12925
+ function mkTimestamp(d) {
12926
+ const ds = d.toISOString().split("T")[0];
12927
+ const pad = (n) => String(n).padStart(2, "0");
12928
+ const HH = pad(d.getUTCHours());
12929
+ const MM = pad(d.getUTCMinutes());
12930
+ const SS = pad(d.getUTCSeconds());
12931
+ const ts = `${HH}:${MM}:${SS}`;
12932
+ return `${ds} @ ${ts}`;
12933
+ }
12934
+ function readBldTxt(bld_dir) {
12935
+ try {
12936
+ const txt = Fs3.readFileSync(Path3.join(bld_dir, "BUILD.md"), { encoding: "utf-8" });
12937
+ return `${txt}
12938
+ * [BUILD ARTIFACTS](../${Path3.basename(bld_dir)}) &thinsp;\u2699\uFE0F
12939
+ `;
12940
+ } catch (e) {
12941
+ console.log(`... skipping 'BUILD.md' inclusion`);
12942
+ }
12943
+ return "";
12944
+ }
12945
+ function readBrdTxt(brd) {
12946
+ const url = `https://raw.githubusercontent.com/em-foundation/emscope/docs-stable/docs/boards/${brd}.md`;
12947
+ const is_win = process.platform === "win32";
12948
+ const cmd = is_win ? "curl.exe" : "curl";
12949
+ const args = is_win ? ["-fsSL", "--tlsv1.2", "--ssl-no-revoke", url] : ["-fsSL", url];
12950
+ try {
12951
+ const txt = ChildProc.execFileSync(cmd, args, { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] });
12952
+ return `${txt}
12953
+ * [BOARD PINOUT](https://github.com/em-foundation/emscope/blob/docs-stable/docs/boards/${brd}.png) &thinsp;\u2699\uFE0F
12954
+ `;
12955
+ } catch (e) {
12956
+ console.log(`... skipping 'BOARD.md' inclusion`);
12957
+ }
12958
+ return "";
12959
+ }
12884
12960
 
12885
12961
  // src/Exporter.ts
12886
12962
  var import_adm_zip = __toESM(require_adm_zip());
@@ -12911,6 +12987,16 @@ function deflateLfs(repo, gpath) {
12911
12987
  import_child_process.default.execFileSync("git", ["lfs", "pull", "--include", gpath], { cwd: repo, stdio: "inherit" });
12912
12988
  import_child_process.default.execFileSync("git", ["lfs", "checkout", gpath], { cwd: repo, stdio: "inherit" });
12913
12989
  }
12990
+ function fetchOid(repo, gpath) {
12991
+ let res = "";
12992
+ try {
12993
+ const out = import_child_process.default.execFileSync("git", ["show", gpath], { cwd: repo, stdio: ["ignore", "pipe", "ignore"] }).toString();
12994
+ const m = out.match(/^\s*oid sha256:([0-9a-f]{64})/m);
12995
+ if (m) res = m[1];
12996
+ } catch {
12997
+ }
12998
+ return res;
12999
+ }
12914
13000
  function findRepoDir(capdir) {
12915
13001
  let repo = "";
12916
13002
  let dir = capdir;
@@ -12951,6 +13037,12 @@ function toggleLfs(capdir, opts) {
12951
13037
  import_fs2.default.rmSync(Capture.workdir(capdir), { recursive: true });
12952
13038
  return;
12953
13039
  }
13040
+ const oid = fetchOid(repo, `:${gpath}`);
13041
+ fail(`'emscope-capture.zip' not yet committed`, !oid);
13042
+ const oid_head = fetchOid(repo, `HEAD:${gpath}`);
13043
+ if (oid_head && oid_head != oid) {
13044
+ restoreLfs(repo, gpath);
13045
+ }
12954
13046
  if (desc_flag) {
12955
13047
  deflateLfs(repo, gpath);
12956
13048
  }
@@ -13027,10 +13119,6 @@ var DATALOSS_THRESHOLD = 500;
13027
13119
  var getMaskedValue = (value, { mask, pos }) => (value & mask) >> pos;
13028
13120
  async function execCapture2(opts) {
13029
13121
  const path_list = await findDevices();
13030
- if (path_list.length == 0) {
13031
- console.error("*** no PPK2 analyzer");
13032
- process.exit(1);
13033
- }
13034
13122
  const progress = new Progress("capturing: ");
13035
13123
  const port = new import_serialport.SerialPort({ path: path_list[0], baudRate: 9600, autoOpen: false });
13036
13124
  await new Promise((resolve, reject) => {
@@ -13040,7 +13128,7 @@ async function execCapture2(opts) {
13040
13128
  port.on("data", () => {
13041
13129
  });
13042
13130
  const cap = Capture.create(opts.capture, opts.duration, "PPK2", opts.voltage);
13043
- const ppk = new PPK2(port, cap);
13131
+ const ppk = new PPK2(port);
13044
13132
  await ppk.getModifiers();
13045
13133
  if (opts.ampereMode) {
13046
13134
  ppk.setMode("ampere");
@@ -13049,11 +13137,26 @@ async function execCapture2(opts) {
13049
13137
  ppk.setSourceVoltage(cap.voltage * 1e3);
13050
13138
  }
13051
13139
  ppk.togglePower("on");
13052
- await ppk.capture(progress);
13140
+ await ppk.record(cap, progress);
13053
13141
  ppk.togglePower("off");
13054
13142
  ppk.close();
13055
13143
  return cap;
13056
13144
  }
13145
+ async function powerOn(voltage) {
13146
+ const path_list = await findDevices();
13147
+ const progress = new Progress("powering: ");
13148
+ const port = new import_serialport.SerialPort({ path: path_list[0], baudRate: 9600, autoOpen: false });
13149
+ await new Promise((resolve, reject) => {
13150
+ port.open((err) => err ? reject(err) : resolve());
13151
+ });
13152
+ await progress.spin(2500);
13153
+ const ppk = new PPK2(port);
13154
+ await ppk.getModifiers();
13155
+ ppk.setMode("source");
13156
+ ppk.setSourceVoltage(voltage * 1e3);
13157
+ ppk.togglePower("on");
13158
+ progress.done();
13159
+ }
13057
13160
  async function findDevices() {
13058
13161
  let res = new Array();
13059
13162
  for (const port of await import_serialport.SerialPort.list()) {
@@ -13063,6 +13166,7 @@ async function findDevices() {
13063
13166
  res.push(port.path);
13064
13167
  }
13065
13168
  }
13169
+ fail("no PPK2 analyzer found", res.length == 0);
13066
13170
  return res;
13067
13171
  }
13068
13172
  function parseMods(mods) {
@@ -13077,7 +13181,6 @@ function parseMods(mods) {
13077
13181
  return res;
13078
13182
  }
13079
13183
  var PPK2 = class {
13080
- #cap;
13081
13184
  #port;
13082
13185
  #modifiers = {
13083
13186
  r: [1031.64, 101.65, 10.15, 0.94, 0.043],
@@ -13103,15 +13206,14 @@ var PPK2 = class {
13103
13206
  #afterSpike;
13104
13207
  #consecutiveRangeSample;
13105
13208
  #currentVdd = 0;
13106
- constructor(port, cap) {
13107
- this.#cap = cap;
13209
+ constructor(port) {
13108
13210
  this.#port = port;
13109
13211
  }
13110
- async capture(progress) {
13212
+ async record(cap, progress) {
13111
13213
  await progress.spin(2e3);
13112
13214
  this.startMeasurement();
13113
13215
  return await new Promise((resolve) => {
13114
- const raw = Buffer.allocUnsafe(this.#cap.duration * 1e5 * 4);
13216
+ const raw = Buffer.allocUnsafe(cap.duration * 1e5 * 4);
13115
13217
  let offset = 0;
13116
13218
  this.#port.on("data", (buf) => {
13117
13219
  progress.update(`${(offset / 1e5 / 4).toFixed(3)} s`);
@@ -13124,7 +13226,7 @@ var PPK2 = class {
13124
13226
  });
13125
13227
  this.stopMeasurement();
13126
13228
  for (const adcVal of new Uint32Array(raw.buffer, raw.byteOffset, raw.length / 4)) {
13127
- this.#cap.current_ds.add(this.#handleRawDataSet(adcVal) / 1e6);
13229
+ cap.current_ds.add(this.#handleRawDataSet(adcVal) / 1e6);
13128
13230
  }
13129
13231
  resolve();
13130
13232
  }
@@ -13269,6 +13371,10 @@ var PPK2 = class {
13269
13371
  async function exec3(opts) {
13270
13372
  let c;
13271
13373
  if (opts.js220) {
13374
+ if (opts.ppk2Supply) {
13375
+ const voltage = opts.ppk2Supply === true ? 3.3 : Number(opts.ppk2Supply);
13376
+ await powerOn(voltage);
13377
+ }
13272
13378
  c = await execCapture(opts);
13273
13379
  } else if (opts.ppk2) {
13274
13380
  c = await execCapture2(opts);
@@ -13290,7 +13396,7 @@ function saveSignal(cap, cname, span, markers = []) {
13290
13396
  const msig_I = cap.current_sig.window(span.width, span.offset).toSignal();
13291
13397
  const msig_V = cap.voltage_sig.window(span.width, span.offset).toSignal();
13292
13398
  const data_V = msig_V.data.length > 0 ? msig_V.data : new Float32Array(msig_I.data.length).fill(cap.avg_voltage);
13293
- writer.store(msig_I.data, data_V, msig_I.sample_rate);
13399
+ writer.store(msig_I.data, data_V, msig_I.sample_rate, Math.trunc(cap.creation_date.getTime() / 1e3));
13294
13400
  let cobj = {
13295
13401
  id: "joulescope.ui.waveform_widget",
13296
13402
  version: "1.0",
@@ -13346,7 +13452,8 @@ var WriterAux = class _WriterAux {
13346
13452
  this.#jfile.close();
13347
13453
  infoMsg(`wrote '${this.cname}.jls'`);
13348
13454
  }
13349
- store(f32_I, f32_V, sample_rate) {
13455
+ store(f32_I, f32_V, sample_rate, utc) {
13456
+ utc = Date.now() / 1e3;
13350
13457
  const sdef = {
13351
13458
  source_id: 1,
13352
13459
  name: "mysource",
@@ -13374,11 +13481,13 @@ var WriterAux = class _WriterAux {
13374
13481
  };
13375
13482
  this.#jfile.signalDef(sigdef);
13376
13483
  this.#jfile.writeF32(1, f32_I);
13484
+ this.#jfile.setTime(1, utc);
13377
13485
  sigdef.signal_id = 2;
13378
13486
  sigdef.name = "voltage";
13379
13487
  sigdef.units = "V";
13380
13488
  this.#jfile.signalDef(sigdef);
13381
13489
  this.#jfile.writeF32(2, f32_V);
13490
+ this.#jfile.setTime(2, utc);
13382
13491
  const f32_W = new Float32Array(f32_I.length);
13383
13492
  for (let i = 0; i < f32_W.length; i++) {
13384
13493
  f32_W[i] = f32_I[i] * f32_V[i];
@@ -13388,6 +13497,7 @@ var WriterAux = class _WriterAux {
13388
13497
  sigdef.units = "W";
13389
13498
  this.#jfile.signalDef(sigdef);
13390
13499
  this.#jfile.writeF32(3, f32_W);
13500
+ this.#jfile.setTime(3, utc);
13391
13501
  }
13392
13502
  };
13393
13503
 
@@ -13397,7 +13507,8 @@ var import_os = __toESM(require("os"));
13397
13507
  var import_path3 = __toESM(require("path"));
13398
13508
  function exec4(opts) {
13399
13509
  const cap = Capture.load(opts.capture);
13400
- const aobj = cap.analysis ?? analyze(cap);
13510
+ fail(`no prior analysis: run 'emscope scan ...'`, cap.analysis === void 0);
13511
+ const aobj = cap.analysis;
13401
13512
  if (opts.eventInfo) {
13402
13513
  printEventInfo(cap, aobj.events);
13403
13514
  return;
@@ -13458,34 +13569,33 @@ function printEventInfo(cap, markers) {
13458
13569
  for (const m of markers) {
13459
13570
  const egy = cap.energyWithin(m);
13460
13571
  avg += egy * scale;
13461
- const dur = (cap.current_sig.offToSecs(m.width) * 1e3).toFixed(3).padStart(7, " ");
13462
- const dur_s = cap.current_sig.offToSecs(m.width);
13572
+ const dur = (cap.current_sig.offToSecs(m.width) * 1e3).toFixed(2).padStart(5, " ");
13463
13573
  const off_s = cap.current_sig.offToSecs(m.offset).toFixed(2).padStart(5, " ");
13464
- infoMsg(`${lab} :: time = ${off_s} s, energy = ${joules(egy)}, duration = ${toEng(dur_s, "s")}`);
13574
+ infoMsg(`${lab} :: time = ${off_s} s, energy = ${uJoules(egy)}, duration = ${dur} s`);
13465
13575
  lab = String.fromCharCode(lab.charCodeAt(0) + 1);
13466
13576
  }
13467
13577
  infoMsg("----");
13468
- infoMsg(`average energy over ${markers.length} event(s): ${joules(avg)}`);
13578
+ infoMsg(`average energy over ${markers.length} event(s): ${uJoules(avg)}`);
13469
13579
  }
13470
13580
  function printResults(cap, aobj, ev_rate, score_only) {
13471
13581
  const sleep_pwr = aobj.sleep.avg * cap.avg_voltage;
13472
- score_only || infoMsg(`event period: ${secsToHms(ev_rate)}`);
13473
- score_only || infoMsg(`average sleep power: ${toEng(sleep_pwr, "W")}`);
13582
+ score_only || infoMsg(`event period: ${secsToHms(ev_rate)}`);
13583
+ score_only || infoMsg(`average sleep power: ${toEng(sleep_pwr, "W")}`);
13474
13584
  score_only || infoMsg("----");
13475
13585
  const egy_1s = cap.energyWithin(aobj.span) / cap.current_sig.offToSecs(aobj.span.width);
13476
13586
  const egy_1e = egy_1s - sleep_pwr * 1;
13477
13587
  const egy_1c = sleep_pwr * ev_rate + egy_1e;
13478
- score_only || infoMsg(`representative event: ${joules(egy_1e)}`);
13479
- score_only || infoMsg(`energy per period: ${joules(egy_1c)}`);
13588
+ score_only || infoMsg(`representative event: ${uJoules(egy_1e)}`);
13589
+ score_only || infoMsg(`energy per period: ${uJoules(egy_1c)}`);
13480
13590
  const egy_1d = egy_1c * 86400 / ev_rate;
13481
- score_only || infoMsg(`energy per day: ${joules(egy_1d)}`);
13591
+ score_only || infoMsg(`energy per day: ${joules(egy_1d)}`);
13482
13592
  const egy_1m = egy_1d * 30;
13483
13593
  const ems = 2400 / egy_1m;
13484
13594
  score_only || infoMsg("----");
13485
13595
  infoMsg(`${ems.toFixed(2)} EM\u2022eralds`);
13486
13596
  }
13487
13597
  function printSleepInfo(cap, si) {
13488
- infoMsg(`sleep current = ${amps(si.avg)} @ ${cap.avg_voltage.toFixed(2)} V, standard deviation = ${amps(si.std)}`);
13598
+ infoMsg(`sleep current = ${uAmps(si.avg).trim()} @ ${cap.avg_voltage.toFixed(1)} V, standard deviation = ${uAmps(si.std).trim()}`);
13489
13599
  }
13490
13600
 
13491
13601
  // node_modules/commander/esm.mjs
@@ -13509,8 +13619,8 @@ var {
13509
13619
  var CAP = ["-c --capture <directory path>", "working capture directory", "."];
13510
13620
  var VERS = version();
13511
13621
  var CMD = new Command("emscope").option("-C, --capture-glob [name pattern]", `apply this command to each matching child capture directory (default "**")`).version(VERS);
13512
- CMD.command("grab").description("record power signals with an attached capture device").option(CAP[0], CAP[1], CAP[2]).option("-d --duration <value>", "capture duration in seconds", parseFloat, 3).option("-J --js220", "use a Joulescope JS220 device").option("-P --ppk2", "use a Nordic PPK2 device").addOption(new Option("-A --ampere-mode", "enable PPK ampere mode").conflicts(["sourceMode", "js220"])).addOption(new Option("-S --source-mode", "enable PPK source mode").default(true).conflicts(["ampereMode", "js220"])).addOption(new Option("-v, --voltage [value]", "source voltage").argParser(parseFloat).default(3.3).conflicts("js220")).action((opts, cmd) => execCmd(exec3, opts, cmd.parent.opts()));
13513
- CMD.command("scan").description("analyze captured data and locate active events").option(CAP[0], CAP[1], CAP[2]).option("-d, --min-duration <milliseconds>", "remove events whose duration is under a threshold", parseFloat).option("-e, --min-energy <microJoules>", "remove events whose energy is under a threshold", parseFloat).option("-g, --gap <milliseconds>", "combine adjacent events whose gap is under a threshold", parseFloat).option("-t --trim <event count>", "remove extra events", parseFloat).action((opts, cmd) => execCmd(exec, opts, cmd.parent.opts()));
13622
+ CMD.command("grab").description("record power signals with an attached capture device").option(CAP[0], CAP[1], CAP[2]).option("-d --duration <value>", "capture duration in seconds", parseFloat, 3).option("-J --js220", "use a Joulescope JS220 device").option("-P --ppk2", "use a Nordic PPK2 device").addOption(new Option("-A --ampere-mode", "enable PPK ampere mode").conflicts(["sourceMode", "js220"])).addOption(new Option("-S --source-mode", "enable PPK source mode").default(true).conflicts(["ampereMode", "js220"])).addOption(new Option("-p, --ppk2-supply [voltage]", "supply JS220 voltage from a PPK").argParser(parseFloat).conflicts("ppk2")).addOption(new Option("-v, --voltage [value]", "source voltage").argParser(parseFloat).default(3.3).conflicts("js220")).action((opts, cmd) => execCmd(exec3, opts, cmd.parent.opts()));
13623
+ CMD.command("scan").description("analyze captured data and locate active events").option(CAP[0], CAP[1], CAP[2]).option("-d, --min-duration <milliseconds>", "remove events whose duration is under a threshold", parseFloat).option("-e, --min-energy <microJoules>", "remove events whose energy is under a threshold", parseFloat).option("-g, --gap <milliseconds>", "combine adjacent events whose gap is under a threshold", parseFloat).option("-t --trim <event count>", "remove extra events", parseFloat).option("--refresh", "(re-)scan using the last set of options").action((opts, cmd) => execCmd(exec, opts, cmd.parent.opts()));
13514
13624
  CMD.command("view").description("present captured data in different formats").option(CAP[0], CAP[1], CAP[2]).option("-e --event-info", "characterize power consumption when active").option("-j --jls-file [event ID]", "generate a Joulescope .jls file containing events").option("-s --sleep-info", "characterize power consumption when inactive").option("-w --what-if [event period]", `extrapolate results for a given event period (default: '00:00:01')`, parseHms).option("--score", "only print the EM\u2022eralds benchmark score").action((opts, cmd) => execCmd(exec4, opts, cmd.parent.opts()));
13515
13625
  CMD.command("pack").description(`bundle captured data into an 'emscope.zip' file`).option(CAP[0], CAP[1], CAP[2]).option("-a --about-file", `update the 'ABOUT.md' file only`).option("-s,--status", `status of the 'emscope.zip' file`).option("-u --unpack", `deflate the 'emscope.zip' file for local use`).option("-z --zip-file", `generate the 'emscope.zip' file`).option("--restore", `restores the 'emscope.zip' LFS descriptor (debug only)`).action((opts, cmd) => execCmd(exec2, opts, cmd.parent.opts()));
13516
13626
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@em-foundation/emscope",
3
- "version": "25.1.0",
3
+ "version": "25.3.0",
4
4
  "description": "benchmark energy efficiency in resource-constrained embedded systems",
5
5
  "bin": {
6
6
  "emscope": "dist/emscope"
@@ -32,14 +32,14 @@
32
32
  "bugs": {
33
33
  "url": "https://github.com/em-foundation/emscope/issues"
34
34
  },
35
- "homepage": "https://github.com/em-foundation/emscope/blob/main/docs/ReadMore.md",
35
+ "homepage": "https://github.com/em-foundation/emscope/blob/docs-stable/docs/ReadMore.md",
36
36
  "dependencies": {
37
37
  "@serialport/bindings-cpp": "^13.0.1",
38
38
  "@types/adm-zip": "^0.5.7",
39
39
  "@types/js-yaml": "^4.0.9",
40
40
  "adm-zip": "^0.5.16",
41
41
  "commander": "^14.0.0",
42
- "jls-writer": "https://github.com/em-foundation/npm-packages/releases/download/resources/jls-writer-0.0.2.tgz",
42
+ "jls-writer": "https://github.com/em-foundation/npm-packages/releases/download/resources/jls-writer-0.0.3.tgz",
43
43
  "joulescope_driver": "^1.10.0",
44
44
  "js-yaml": "^4.1.0",
45
45
  "picomatch": "^4.0.3",
@@ -50,4 +50,4 @@
50
50
  "@types/picomatch": "^4.0.2",
51
51
  "esbuild": "^0.25.8"
52
52
  }
53
- }
53
+ }